diff --git a/.agents/skills/nebula-lab/SKILL.md b/.agents/skills/nebula-lab/SKILL.md
index 9f9c908821..c2f2de58c3 100644
--- a/.agents/skills/nebula-lab/SKILL.md
+++ b/.agents/skills/nebula-lab/SKILL.md
@@ -31,6 +31,17 @@ angle — leaves on every side. Slabs pack into per-axis atlases for delivery. I
handoff explicitly: brightness and colour agreement between banks is an acceptance criterion,
not a detail.
+New compiler and sampled-volume deliveries target **500 total retained scene elements**,
+including stars, every optical slab copy, impostors and wrappers. Reserve their actual costs
+before planning XYZ intervals; 500 slabs is not a 500-element scene. Prefer source-backed
+slab emission for unresolved or dense light, and use fewer separate catalogue stars where
+the evidence supports that representation. Never count the same light in both, invent stellar
+depths, or silently discard retained stars to fit the quota. Preserve the saved plan and
+cost receipt for replay and all component mixtures. A planning estimate is not visual
+acceptance; keep the actual brightness/detail/handoff gates. Historical pinned deliveries
+remain unchanged and report when over budget. See
+`labs/nebula/docs/emission-compiler.md#automatic-layer-budget` before changing this step.
+
## Packages and boundaries
`labs/nebula/packages/{lab,volume-core,volume-bake,reconstruction,volume-viewer}`.
@@ -71,7 +82,28 @@ Pick by what the evidence supports, not by habit.
Two-scale in one line: broad light is `gain(x,y) × prior(x,y,z)` with `gain = fraction × blur(image) / blur(column)`; finite components fit the residual at prior-supported depths, so image brightness scales the envelope and never moves it in depth. Example recipe: `labs/nebula/models/smc/constrained/emission-envelope-ellipsoid.json`, with its method note beside it.
-**Lenses:** every image recolours one shared geometry, so switching lens changes colour, never shape. **The prior is a hypothesis, never a measurement** — prefer one that scores better against observations; for the SMC a VMC-constrained ellipsoid beat the tidal simulation (withheld deviance 0.457 vs 0.601).
+**RGB-only lenses:** every image recolours one shared geometry, so switching lens changes colour, never shape. **The prior is a hypothesis, never a measurement** — prefer one that scores better against observations; for the SMC a VMC-constrained ellipsoid beat the tidal simulation (withheld deviance 0.457 vs 0.601).
+
+## Numerical alignment before image inspection
+
+Diagnose alignment numerically before judging screenshots or rebaking. Use the configured
+lenses as independent checks of the shared coordinate transport; different spectral structures
+and brightness are expected, so do not force their pixels to match.
+
+- Check registered landmarks or matched stars in the common sky frame: translation, scale,
+ rotation, handedness, footprints and held-out residuals in pixels/arcseconds. Verify the same
+ points through source, prepared geometry and camera projection.
+- Compare shared geometry/alpha across RGB-only lenses. For declared component mixtures,
+ compare the shared frame, slab intervals and layout while preserving legitimate support
+ differences. Use spatial residuals, not histogram agreement, to establish alignment.
+- Separate registration error from material, sampling and browser compositing error. Compare
+ analytic projections, decoded baked values and rendered pixel measurements at the same
+ camera. Fix the owner identified by those measurements; do not compensate with a display
+ offset or assume that every failed image metric proves misregistration.
+- Run capture analysis locally and return compact numerical reports. Keep captures on disk;
+ do not routinely load them into model context. Inspect an image only for a named question
+ that the numbers cannot settle, a required final visual check, or an explicit user request;
+ use the smallest useful crop and fewest images.
## Matching a lens to its image
@@ -143,6 +175,17 @@ server route you can call directly, so an agent sees exactly what a person sees.
| --- | --- | --- |
| Levels | `GET /__nebula/reconstruction-levels?resultId=` | per-channel histograms, signed delta, transfer curve, percentile ratios |
| Difference map | `GET /__nebula/reconstruction-difference?resultId=` (`&format=png` for the image) | render − image on luminance, over the Earth view: blue too dark, red too bright, clear within tolerance |
+| Radial profile | `GET /__nebula/reconstruction-radial?resultId=` | azimuthally averaged source/render brightness in radial bins from the footprint centroid, per-bin ratio and delta, half-light radius ratio |
+
+**Radial profile answers what the other two cannot: does brightness fall off with radius like the source?**
+Levels is a histogram and spatially blind — it can match p50/p90/p99 exactly while the structure sits in the
+wrong place (a brightness swap between the centre and an annulus conserves the histogram outright). The
+difference map shows *where* the error is but not its radial shape. The radial profile's own control is that
+swap: a permutation of the same pixel values that moves brightness from the core to the rim reads as p50/p90
+ratio 1.00 in Levels while the radial profile's per-bin ratio runs from ~0 at the centre to tens at the rim
+and its half-light radius is pushed outward. A pure scale error (render radially compressed or expanded) is
+caught by the half-light radius ratio instead. Highest value for a centrally concentrated, near-spherical
+subject.
**Read the difference map for *where*, the levels for *how much*.** On the LMC it showed the bar
blue (core too dark, the opacity shoulder) and the body a red ring (mid-tones too bright), which
diff --git a/.github/workflows/universe.yml b/.github/workflows/universe.yml
index 94881af093..1472396b40 100644
--- a/.github/workflows/universe.yml
+++ b/.github/workflows/universe.yml
@@ -409,6 +409,7 @@ jobs:
run: >-
node --test tools/prepare-context-availability.test.mts
site/test/prepared-context-navigation.test.mts
+ site/test/galactic-focus.test.mts
site/test/prepared-focus-card.test.mts site/test/volume-presentation.test.mts
- name: Restore environment images and prepare minimap
run: |
@@ -591,7 +592,8 @@ jobs:
node labs/nebula/run.mts test
overlay-placement overlay-registration overlay-alignment
field-tilt emission-window component-material material
- point-projection cloud-inspection compact
+ point-projection cloud-inspection compact shape-cloud-camera
+ nebula-frame bake compiler-sampling render-element-budget retained-material-bank optical-composite-preparation
detect-shapes structure-wavelets structure-map ridge-graph
material-solver shape-prior solver
acquisition footprint image-links image-ranking
@@ -601,6 +603,8 @@ jobs:
workspace-sections workspace-portals workspace-objects camera-model-panel image-appearance-panel
catalogue object-browser papers survey-images
geometry-model observation-alignment structures-model
+ build lens-levels lens-difference lens-radial lens-radial-panel
+ registration photometric-mge photometric-prior compiler-defaults simulation-guided compiler-published
ci-guard:
name: Report the first CI failure
diff --git a/.gitignore b/.gitignore
index 78f1784a17..1627840826 100644
--- a/.gitignore
+++ b/.gitignore
@@ -215,6 +215,9 @@ src/objects/*/source/**/*.bpc
# the accepted bytes instead of replaying them (`--verify-replay` still can).
/labs/nebula/models/*/candidates/prepared/*.webp
/labs/nebula/models/*/clouds-observation/source/target.png
+# Reference derivatives rebuilt by `acquire-images` from the hash-pinned publisher TIFF in the ignored cache.
+# Their `.webp.json` sidecars stay tracked: the pins and the interpretation are the record, the pixels are not.
+/labs/nebula/sources/*.webp
# Reacquirable distant-world paper originals; acquisition recipes pin exact bytes.
/src/objects/oumuamua/source/reference/*.html
/src/objects/sedna/source/reference/*.html
@@ -293,6 +296,7 @@ src/objects/*/source/**/*.bpc
/src/objects/m8/prepared/
/src/objects/smc/prepared/
/src/objects/lmc/prepared/
+/src/objects/omega-centauri/prepared/
/src/objects/betelgeuse-shell/.prepared-*/
/src/objects/hd-181327-disc/.prepared-*/
/src/objects/m42/.prepared-*/
@@ -303,6 +307,7 @@ src/objects/*/source/**/*.bpc
/src/objects/lmc/.prepared-*/
/src/objects/m45/.prepared-*/
/src/objects/m8/.prepared-*/
+/src/objects/omega-centauri/.prepared-*/
# Binary kernels in shared SPICE kernel banks are restored by tools/spice/kernel-bank.mts; text kernels are committed.
src/spice/**/*.bsp
src/spice/**/*.bc
diff --git a/docs/README.md b/docs/README.md
index c2c9025a61..b18052882a 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -21,6 +21,7 @@ For a body's sources, processing, evidence and known problems, read its
| Querying VO archives, selecting bounded science products and retaining acquisition evidence | [VO observation access](vo-observation-access.md) |
| Which upstream packages own a mechanical boundary here, and which contracts stay ours | [Astronomy package ownership](astronomy-package-ownership.md) |
| One example picture per telescope, each made from a product that telescope's toolkit produced here | [Telescope examples](telescope-examples.md) |
+| Pinned sources, public commands and retained outputs for the 18 telescope data families | [Telescope family examples](telescope-family-examples/README.md) |
| Exoplanet light curves to maps: eigencurve fitting and its checks | [Eclipse mapping](eclipse-mapping.md) |
| JWST images to sky band composites: MAST programs, the re-run level-3 stage, its oracle and depth from a model | [JWST imaging](jwst-imaging.md) |
| What JWST's public archive holds by observing mode, what this project can already reduce, and which shipped objects JWST has observed | [JWST ledger](jwst-ledger.md) |
diff --git a/docs/provenance/investigation-index.md b/docs/provenance/investigation-index.md
index 21f6e47d76..37759fb516 100644
--- a/docs/provenance/investigation-index.md
+++ b/docs/provenance/investigation-index.md
@@ -4,7 +4,7 @@ Generated by `pnpm investigations:index`. Every unresolved or deferred decision
decisions, not scientific readiness.
**Ledgers:** 496 of 496 catalogued objects.
-**Open decisions:** 1023 (900 unresolved, 123 deferred).
+**Open decisions:** 1027 (901 unresolved, 126 deferred).
## Where the reopening evidence would appear
@@ -13,7 +13,7 @@ The source each decision examined. Watching these is how a decision reopens.
| Source | Open | Bodies |
| --- | ---: | --- |
| damit.cuni.cz | 257 | abundantia, adelheid, aegina, aethra, aglaja, alexandra, +168 more |
-| arxiv.org | 72 | adelheid, aegina, aemilia, agenor, aglaja, alkmene, +55 more |
+| arxiv.org | 73 | adelheid, aegina, aemilia, agenor, aglaja, alkmene, +56 more |
| doi.org | 42 | bienor, braille, ce-tauri, chariklo, comet-1p, comet-209p, +28 more |
| sbnarchive.psi.edu | 29 | atlas, comet-109p, comet-153p, comet-167p, comet-17p, comet-21p, +23 more |
| tilmanndenk.de | 23 | albiorix, bebhionn, bergelmir, bestla, erriapus, fornjot, +16 more |
@@ -26,11 +26,12 @@ The source each decision examined. Watching these is how a decision reopens.
| www.hou.usra.edu | 5 | ariel, hydra, nix, varuna, annefrank |
| astro.troja.mff.cuni.cz | 4 | eurybates, orus, patroclus, polymele |
| echo.jpl.nasa.gov | 4 | comet-209p, comet-2p, comet-8p, nereus |
+| zenodo.org | 4 | hd-189733b, wasp-43b, ganymede, omega-centauri |
| space.jpl.nasa.gov | 3 | juliet, nereid, ophelia |
| www.lpi.usra.edu | 3 | asteroid-1998-ml14, comet-1p, methone |
-| zenodo.org | 3 | hd-189733b, wasp-43b, ganymede |
| almascience.eso.org | 2 | r-doradus, haumea |
| archive.eso.org | 2 | betelgeuse, r-doradus |
+| cdsarc.cds.unistra.fr | 2 | smc, omega-centauri |
| github.com | 2 | dinkinesh, wasp-43b |
| naif.jpl.nasa.gov | 2 | annefrank, dinkinesh |
| opus.pds-rings.seti.org | 2 | phoebe, metis |
@@ -43,7 +44,6 @@ The source each decision examined. Watching these is how a decision reopens.
| archives.esac.esa.int | 1 | comet-67p |
| asc-planetarynames-data.s3.us-west-2.amazonaws.com | 1 | dactyl |
| astrogeology.usgs.gov | 1 | ida |
-| cdsarc.cds.unistra.fr | 1 | smc |
| comet-toolbox.com | 1 | lutetia |
| data.caltech.edu | 1 | titan |
| data.darts.isas.jaxa.jp | 1 | itokawa |
@@ -166,6 +166,7 @@ The source each decision examined. Watching these is how a decision reopens.
| `dark-red-spot-colour-variation` | 1 | haumea | A published surface map or rotationally resolved colour model fixes the region's extent and contrast in a body-fixed frame. |
| `dated-report` | 1 | pluto | A dated run for this Pluto version is recorded in the package. |
| `deep-impact-li-2013-photometric-normalization` | 1 | comet-9p | A verified formulation and matching filter calibration improve both retained terrain coverage and independent overlaps. |
+| `delivery-blocker` | 1 | omega-centauri | The unchanged actual-renderer handoff gate and main-app visual acceptance pass, including acceptable close-zoom presentation. Navigation, context visibility, cold replay and R2 restoration do not waive these requirements. |
| `deposit-temperature-conversion` | 1 | wasp-43b | The conversion code or stellar spectrum behind the deposited maps is published. |
| `detailed-shape-dem-and-geology` | 1 | belinda | A Belinda source release provides the missing downloadable mapped data, projection, measured quantity and coverage for direct comparison with the selected inputs. |
| `diffuse-material` | 1 | helix | Audit source separation and finite emission with positive source evidence, retaining real knots and verified compact anchors, then inspect the same spectral front/side views. |
@@ -206,6 +207,7 @@ The source each decision examined. Watching these is how a decision reopens.
| `hst-photometry-and-keck-near-infrared-photometry` | 1 | juliet | A spatially resolved Juliet measurement supplies original spectral or image samples with body-fixed coordinates and enough coverage to qualify a mapped lens. |
| `hst-photometry-ground-based-near-infrared-studies-an` | 1 | cordelia | A spatially resolved Cordelia measurement supplies original spectral or image samples with body-fixed coordinates and enough coverage to qualify a mapped lens. |
| `hst-photometry-recovery-and-observer-account` | 1 | ophelia | A spatially resolved Ophelia measurement supplies original spectral or image samples with body-fixed coordinates and enough coverage to qualify a mapped lens. |
+| `hst-registration-companion` | 1 | omega-centauri | Independent absolute astrometry is required beyond the existing relative native-image registration. |
| `hubble-companion-follow-up` | 1 | deedee | A published companion detection or resolved shape product changes the system interpretation. |
| `image-to-shape-feature-registration` | 1 | comet-1p | Independent surface features with a verified model frame validate the photographic placement within stated source uncertainty. |
| `interacting-orbit-2024` | 1 | hiiaka | The interacting solution is acquired with its frame, epoch and uncertainty and independently checked for the application epoch. |
@@ -259,6 +261,7 @@ The source each decision examined. Watching these is how a decision reopens.
| `middle-axis-uncertainty-discrepancy` | 1 | haumea | An erratum or a later publication settles the middle-axis uncertainty. |
| `missing-sample-continuation` | 1 | sun | A synoptic release supplies observed polar coverage for these instruments. |
| `mission-shape-publications` | 1 | dinkinesh | A released numeric shape, backplane, control-point or camera bundle associated with one of these papers. |
+| `modern-inclination` | 1 | omega-centauri | A coherent photometric/kinematic refit supports combining the newer inclination with a compatible projected profile. |
| `mosaic-coverage-mask` | 1 | steins | A source validity mask or independent per-pixel coverage/registration reference can replace or qualify the documented heuristic. |
| `mp3c-thermal-size-comparison` | 1 | abundantia | The individual source methods and observation geometry can be compared against model 4625, or an occultation, resolved outline or shape-aware thermophysical fit supplies a volume calibration. |
| `mps-other-presentations` | 1 | comet-1p | A distinct photographed region and independent registration demonstrate useful new coverage. |
@@ -339,7 +342,7 @@ The source each decision examined. Watching these is how a decision reopens.
| `shape-pole-rotation` | 1 | comet-3i | A lightcurve inversion, pole solution or rotation period is published. |
| `shape-uniqueness` | 1 | asteroid-2002-tx300 | A multi-chord occultation or resolved imaging determines the third axis. |
| `shell-depth-approximation` | 1 | milky-way | A source supplies measured cloud depths that can replace the authored shell. |
-| `shell-focus-link` | 1 | smc | The initial-focus payload resolves its host chain, or host validation moves off the single-object fragment. |
+| `shell-focus-link` | 1 | smc | The direct SMC browser inspection runs against a complete installed runtime closure. |
| `side-view-limit` | 1 | m2-9 | Improve the owning symmetry/material assumptions against fixed views without masking the defect through exposure or a fabricated depth field. |
| `sio-band` | 1 | betelgeuse | A reduction that keeps the SiO channels unflagged, or a stated closure-phase error model, fits them at reduced chi-squared near 1. |
| `sio-far-emission` | 1 | betelgeuse-shell | A self-calibrated SiO cube of this member, or another epoch of the line, which would show whether the northern emission and its continuum peak persist. |
@@ -396,13 +399,14 @@ The source each decision examined. Watching these is how a decision reopens.
| `vmc-intake` | 1 | smc | The VMC intake is re-acquired with the wrap-corrected match set and the shape fit is re-run against it. |
| `voyager-clear-candidates` | 1 | nereid | A controlled reconstruction demonstrates independent body-fixed registration and useful surface information at the native sampling, or a finer observation becomes available. |
| `voyager-pds-iss` | 1 | belinda | A Belinda observation or controlled reconstruction demonstrates useful native surface detail, body-fixed geometry and independent registration beyond the cited sampling or coverage limits. |
+| `wilson-fit` | 1 | omega-centauri | A specified model comparison justifies implementing the correct Wilson distribution-function solution. |
| `wisp-and-physical-arrays` | 1 | m45 | Acquire the original numerical observations with masks, uncertainty and usable rights before implementing a physical scattering or layered-depth operator. |
| `zubarev-2025-mosaic` | 1 | hyperion | Its pixel or model release becomes publicly downloadable. |
## Backlog: no external source recorded
-478 of these open decisions name no source outside this repository, so nothing says where to look
-(641 with the excluded decisions, which the ledger test tracks). Give each one the archive,
+479 of these open decisions name no source outside this repository, so nothing says where to look
+(642 with the excluded decisions, which the ledger test tracks). Give each one the archive,
deposit or paper it was checked against, or record that its next move is work here.
| Waiting on | Open | Bodies |
@@ -457,6 +461,7 @@ deposit or paper it was checked against, or record that its next move is work he
| `d-g-e-ring-profiles` | 1 | saturn |
| `daily-viirs-imagery` | 1 | earth |
| `dated-report` | 1 | pluto |
+| `delivery-blocker` | 1 | omega-centauri |
| `detailed-shape-dem-and-geology` | 1 | belinda |
| `diffuse-material` | 1 | helix |
| `disconnected-color-encounters` | 1 | prometheus |
diff --git a/docs/telescope-family-examples/README.md b/docs/telescope-family-examples/README.md
index 70cb4b893d..23fcb66123 100644
--- a/docs/telescope-family-examples/README.md
+++ b/docs/telescope-family-examples/README.md
@@ -1,28 +1,28 @@
# Telescope family examples
-This directory pins one compact, reviewable public-path example for every F01–F18 family. `manifest.json` is the source of truth: every row records the real source URL and byte pin, exact CLI command, committed output pin, product-record/readback proof, owner, independent check, and scientific limit.
+The [test fixtures](../../tests/fixtures/telescope-family-examples/) pin one compact, reviewable public-path example for every F01–F18 family. The [manifest](../../tests/fixtures/telescope-family-examples/manifest.json) is the source of truth: every row records the real source URL and byte pin, exact CLI command, committed output pin, product-record/readback proof, owner, independent check, and scientific limit.
Each row carries `exampleProven: true` and `proposalBaseline.status: "complete"` because its family baseline is now executable through a public owner. The examples remain deliberately bounded: completeness applies to the recorded format profile and operations, not every format that could belong to the family.
| Family | Review artifact | Public path | What it proves |
| --- | --- | --- | --- |
-| F01 | `artifacts/F01.csv` | `telescope export` | Native 2D image values |
-| F02 | `artifacts/F02.json` | `telescope family-run` | Mixed time, Stokes and spectral slicing with FITS/WCS context |
-| F03 | `artifacts/F03.csv` | `telescope family-run` | Standalone spectrum export |
-| F04 | `artifacts/F04.json` | `telescope family-run` | Pinned slit-scan inspection |
-| F05 | `artifacts/F05.csv` | `telescope family-run` | Discrete photometry export |
-| F06 | `artifacts/F06.csv` | `telescope family-run` | Explicit time selection |
-| F07 | `artifacts/F07.csv` | `telescope family-run` | Native dynamic-spectrum window |
-| F08 | `artifacts/F08.json` | `telescope family-run` | Typed table inspection |
-| F09 | `artifacts/F09.csv` | `telescope family-run` | Astrometry export |
-| F10 | `artifacts/F10.csv` | `telescope family-run` | Selected event rows with lineage |
-| F11 | `artifacts/F11.json` | `telescope family-run` | Selected UVFITS visibilities with package-owned UV preview |
-| F12 | `artifacts/F12.json` | `telescope family-run` | OIFITS V2 diagnostics |
-| F13 | `artifacts/F13.json` | `telescope family-run` | Declared Stokes spectrum and explicit polarization policy |
-| F14 | `artifacts/F14.json` | `telescope family-run` | Native HEALPix NESTED pixel selection |
-| F15 | `artifacts/F15.json` | `telescope family-run` | Native delay-Doppler profile |
-| F16 | `artifacts/F16.json` | `telescope family-run` | Existing physical point-field inspection |
-| F17 | `artifacts/F17.json` | `telescope family-run` | Pinned NEAR MSI raw/calibrated/label closure |
-| F18 | `artifacts/F18.json` | `telescope family-run` | Exact compound-member enumeration |
+| F01 | [F01.csv](../../tests/fixtures/telescope-family-examples/artifacts/F01.csv) | `telescope export` | Native 2D image values |
+| F02 | [F02.json](../../tests/fixtures/telescope-family-examples/artifacts/F02.json) | `telescope family-run` | Mixed time, Stokes and spectral slicing with FITS/WCS context |
+| F03 | [F03.csv](../../tests/fixtures/telescope-family-examples/artifacts/F03.csv) | `telescope family-run` | Standalone spectrum export |
+| F04 | [F04.json](../../tests/fixtures/telescope-family-examples/artifacts/F04.json) | `telescope family-run` | Pinned slit-scan inspection |
+| F05 | [F05.csv](../../tests/fixtures/telescope-family-examples/artifacts/F05.csv) | `telescope family-run` | Discrete photometry export |
+| F06 | [F06.csv](../../tests/fixtures/telescope-family-examples/artifacts/F06.csv) | `telescope family-run` | Explicit time selection |
+| F07 | [F07.csv](../../tests/fixtures/telescope-family-examples/artifacts/F07.csv) | `telescope family-run` | Native dynamic-spectrum window |
+| F08 | [F08.json](../../tests/fixtures/telescope-family-examples/artifacts/F08.json) | `telescope family-run` | Typed table inspection |
+| F09 | [F09.csv](../../tests/fixtures/telescope-family-examples/artifacts/F09.csv) | `telescope family-run` | Astrometry export |
+| F10 | [F10.csv](../../tests/fixtures/telescope-family-examples/artifacts/F10.csv) | `telescope family-run` | Selected event rows with lineage |
+| F11 | [F11.json](../../tests/fixtures/telescope-family-examples/artifacts/F11.json) | `telescope family-run` | Selected UVFITS visibilities with package-owned UV preview |
+| F12 | [F12.json](../../tests/fixtures/telescope-family-examples/artifacts/F12.json) | `telescope family-run` | OIFITS V2 diagnostics |
+| F13 | [F13.json](../../tests/fixtures/telescope-family-examples/artifacts/F13.json) | `telescope family-run` | Declared Stokes spectrum and explicit polarization policy |
+| F14 | [F14.json](../../tests/fixtures/telescope-family-examples/artifacts/F14.json) | `telescope family-run` | Native HEALPix NESTED pixel selection |
+| F15 | [F15.json](../../tests/fixtures/telescope-family-examples/artifacts/F15.json) | `telescope family-run` | Native delay-Doppler profile |
+| F16 | [F16.json](../../tests/fixtures/telescope-family-examples/artifacts/F16.json) | `telescope family-run` | Existing physical point-field inspection |
+| F17 | [F17.json](../../tests/fixtures/telescope-family-examples/artifacts/F17.json) | `telescope family-run` | Pinned NEAR MSI raw/calibrated/label closure |
+| F18 | [F18.json](../../tests/fixtures/telescope-family-examples/artifacts/F18.json) | `telescope family-run` | Exact compound-member enumeration |
Descriptor member paths are relative to each descriptor, so the examples contain no checkout-specific absolute paths. Runtime `output.product.json` receipts are deliberately not copied here; the manifest points at focused tests that reopen and verify those records.
diff --git a/docs/telescope-family-examples/descriptors/F16.json b/docs/telescope-family-examples/descriptors/F16.json
deleted file mode 100644
index 821fb7509a..0000000000
--- a/docs/telescope-family-examples/descriptors/F16.json
+++ /dev/null
@@ -1,88 +0,0 @@
-{
- "schema": "cssearth-telescope-product-descriptor@1",
- "dataset": {
- "id": "stellar-neighbourhood",
- "target": "stellar-neighbourhood",
- "acquisition": {
- "kind": "repository",
- "identity": "src/objects/stellar-neighbourhood/object.json"
- },
- "producingRecord": "src/objects/stellar-neighbourhood/object.json",
- "sourceClassifications": [
- {
- "term": "physical point field",
- "vocabulary": "cssearth-object@1",
- "version": "1",
- "status": "source"
- }
- ],
- "families": [
- "F16"
- ],
- "profiles": [
- {
- "handlerId": "f16-spatial-physical",
- "profileId": "existing-physical-object@1"
- }
- ]
- },
- "members": [
- {
- "id": "object",
- "path": "../../../src/objects/stellar-neighbourhood/object.json",
- "role": "science",
- "bytes": 906,
- "sha256": "fa4dbdb551af2ecfa85dc4964218f8708bbab60401f8d452a7a5230776c376e1",
- "mediaType": "application/json"
- }
- ],
- "components": [
- {
- "id": "points",
- "name": "stellar-neighbourhood points",
- "families": [
- "F16"
- ],
- "locations": [
- {
- "memberId": "object",
- "path": "object.json"
- }
- ],
- "representation": {
- "kind": "physical-field",
- "topology": "points",
- "samples": null
- },
- "axes": [],
- "columns": [],
- "quantity": {
- "name": "physical point samples",
- "semantics": "Existing prepared physical object; no depth is inferred from an angular or spectral observation."
- },
- "calibration": {
- "state": "model-derived",
- "basis": [
- "Validated by the existing points loader across 7 pinned inputs.",
- "{\"source\":{\"schema\":\"cssearth-source-provenance@1\",\"sources\":[{\"id\":\"catalogue\",\"path\":\"stars-hyg.gxct\",\"sha256\":\"d1574537361ac78d67f32ddf30ac0122e9d52115851f1519cb91bed26dcf8e74\",\"bytes\":4818664,\"key\":\"catalogs/stars-hyg\",\"source\":\"HYG v4.4 (Hipparcos-Yale-Gliese Database, D. Nash / astronexus)\",\"license\":\"CC-BY-SA-4.0\",\"url\":\"https://codeberg.org/astronexus/hyg/raw/branch/main/data/hyg/CURRENT/LICENSE\",\"epoch\":\"ICRS/J2000 equinox; source astrometry primarily Hipparcos-2007 reduction, epoch J1991.25\",\"built\":\"2026-09-02\",\"extra\":{\"count\":109389,\"columns\":[\"posPc\",\"absMag\",\"teffK\",\"radiusSolar\",\"colorIndexBv\",\"hip\",\"name\",\"pmRaMasPerYr\",\"pmDecMasPerYr\"],\"droppedSun\":1,\"droppedNonPositiveDist\":0,\"droppedSentinelDist\":10224,\"totalSourceRows\":119614},\"credit\":\"HYG Stellar Database by David Nash / Astronexus. Prepared derivatives licensed CC-BY-SA-4.0.\",\"modifications\":\"Complete row set reordered into a spatial hierarchy; luminosity-weighted proxies and 32-color display quantization. No astrometry propagation.\"}],\"qualification\":\"Local generated assets; no network publication. HYG astrometry is a local stellar neighbourhood, not a full Milky Way stellar census.\",\"sourceBinding\":{\"kind\":\"catalogued\",\"references\":[{\"catalogueId\":\"hyg-v44\",\"role\":\"material\",\"evidence\":\"src/objects/stellar-neighbourhood/source/provenance.json@8d2f45b58b5ea9a0b69a81242c51aa6e6d6ebcdf#/sources/0\"}]},\"catalogueDisplay\":{\"feature\":\"stars\",\"label\":\"HYG\",\"credit\":\"Star catalogue · David Nash / Astronexus · CC BY-SA 4.0.\",\"description\":\"HYG Stellar Database by David Nash / Astronexus. Prepared derivatives licensed CC-BY-SA-4.0.\",\"footerAliases\":[\"https://codeberg.org/astronexus/hyg/raw/branch/main/data/hyg/CURRENT/LICENSE\"]}},\"catalogueMetadata\":{\"source\":\"HYG v4.4 (Hipparcos-Yale-Gliese Database, D. Nash / astronexus)\",\"epoch\":\"ICRS/J2000 equinox; source astrometry primarily Hipparcos-2007 reduction, epoch J1991.25\",\"built\":\"2026-09-02\",\"sentinelDistPc\":100000,\"sentinelDistDoc\":\"dist == 100000.0 marks an unmeasurable/negative Hipparcos parallax; rows with it are dropped, not shipped.\",\"hipSentinel\":0,\"hipSentinelDoc\":\"hip == 0 means the row has no Hipparcos catalogue number.\",\"teffCalibration\":\"Ballesteros (2012) eq. 14, B-V -> Teff\",\"totalSourceRows\":119614,\"droppedSun\":1,\"droppedNonPositiveDist\":0,\"droppedSentinelDist\":10224,\"survivingRows\":109389},\"qualification\":\"All catalogue rows retained at their recorded astrometry; no proper-motion propagation to the navigation epoch. Internal nodes approximate unresolved luminosity and position; no fixed pixel-error guarantee when the point pool is saturated.\"}"
- ]
- },
- "flags": [],
- "frame": {
- "kind": "physical",
- "name": "sun-icrf",
- "epoch": "JD_TT 2461286.5"
- },
- "dependencyIds": []
- }
- ],
- "dependencies": [],
- "issues": [
- {
- "scope": "component",
- "identity": "points",
- "state": "unknown",
- "reason": "Portable export retains prepared resources and source recipe; raw source datasets are referenced rather than bundled. Bounds: {\"min\":[-1024,-1024,-1024],\"max\":[1024,1024,1024]}."
- }
- ]
-}
diff --git a/labs/nebula/docs/emission-compiler.md b/labs/nebula/docs/emission-compiler.md
index 71cd7c7037..0c876fbaea 100644
--- a/labs/nebula/docs/emission-compiler.md
+++ b/labs/nebula/docs/emission-compiler.md
@@ -30,6 +30,18 @@ After the first successful compile, fit controls apply automatically; only the n
Image-fit error, missing/excess signal and velocity residuals answer different questions. The fit's baseline RMSE is the zero-emission model on the same covered target pixels. Missing/excess fractions refer to normalized display signal; they are not mass fractions. A better projection cannot validate depth, and the display is not a hydrodynamic simulation. Inspect neutral front, oblique and side views as well as textured source comparisons before accepting any visual result.
+## Automatic layer budget
+
+New compiler and sampled-volume deliveries reserve **at most 500 total retained scene elements**, including hidden elements, stars, optical copies, impostors and wrappers. The current single-topology CSS profile reserves 47 elements for delivery overhead and three elements per XYZ slab, plus one per separate star. With no stars, the optimizer receives a maximum of 151 total XYZ slabs. Every retained star reduces that allowance; an impossible allocation fails before baking rather than silently dropping stars. Final app admission also verifies topology and actual delivery overhead.
+
+Prefer source-backed slab emission for unresolved or dense light and fewer separate catalogue stars when the evidence supports it. Do not count the same light twice or infer measured stellar depths from an image. The field and its supported feature scale still determine the fine reference grid. An offline probe groups adjacent reference cells into wider intervals where less depth detail is needed. Each interval integrates every original reference midpoint, including faint emission, before encoding one RGBA8 texture. Material painting uses those same physical intervals and depth samples. This reduces retained planes and texture files; it does not promise proportionally less sampling work.
+
+The planner probes at 64 pixels wide with four image subpixel phases. Spatial variation and displacement from the reference planes estimate the extra error caused by grouping. Dynamic programming chooses complete partitions instead of making greedy splits, which can strand a concentrated feature inside a wide interval. It selects the fewest layers meeting a 1% planning estimate on each axis; if the budget prevents that, it minimizes the worst axis and records `budget-limited`. Sampled component mixtures plan once from an envelope covering every prepared lens, then share that partition.
+
+The saved sampling receipt contains the exact reference grid, intervals, planning report and versioned renderer-cost reservation. All image-only lenses share one topology; component mixtures must not multiply resident topologies beyond the final element allowance. `target-met` describes only this coarse estimate: subpixel features, material differences, RGBA8 rounding and browser compositing still require actual output checks. Inspect front, oblique and side views and compare both banks at each axis handoff. Keep the 5% relative-luminance and 4% normalized-L1 handoff gates unchanged. A failed result remains unaccepted; a completed bake or a small element count cannot qualify it.
+
+Verified compact replay uses its saved partition exactly and never replans. Historical receipts without a renderer-cost reservation retain their original sampling and bytes, including over-budget deliveries; replay reports that limitation. A new cost receipt must still pass validation during replay. New authoring cannot opt out by supplying an old sampling record. Existing repaint-only workflows keep their explicit sampling until separately requalified. The shared planner and grouped baker are available to those preparation owners without runtime sampling or geometry generation.
+
## Reproduce from a clean checkout
Requires the checkout containing the compiler, **Node 22.18+ or a supported newer release, pnpm 10.33.0, Python 3.9–3.12 with venv/pip**, internet access and disk space for native images and results. `python3` below must select that supported Python. The pinned TensorFlow release needs a wheel for the machine's OS/CPU. Run this complete sequence from the repository root; keep an existing lab server alive and omit only the final server command when it already runs.
diff --git a/labs/nebula/models/omega-centauri/README.md b/labs/nebula/models/omega-centauri/README.md
new file mode 100644
index 0000000000..ae21dfbaf1
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/README.md
@@ -0,0 +1,172 @@
+# Omega Centauri (NGC 5139)
+
+**Blocked at visual qualification; not delivered.** Omega Centauri is registered in
+the Lab and uses a published oblate MGE light profile. Bake 1 completed but was
+visually rejected. Bake 2 produced neutral geometry, then failed during lens
+painting. Bake 3 completed for local inspection after the material roundoff fix;
+its unchanged neutral geometry still fails the axis-handoff image gate. A separately
+authorized adaptive-layer bake reduces retained slabs from 779 to 459 but also
+fails that gate; see the layer-optimization experiment below. No app
+promotion, merge, or Omega R2 publication/restoration has been completed.
+
+## Current method
+
+[photometric-mge.json](photometric-mge.json) transcribes the eight projected
+Gaussians in D’Souza & Rix (2013), Table 1, with a pinned source PDF and
+[physical evidence](physical-evidence.json). The selected conditional model uses
+PA 100° east of north, inclination 50°, and distance 5426 ± 47 pc. Its near/far
+tilt sign and six-sigma numerical cutoff are authored; they are not measurements.
+[The method note](mge-method.md) gives units, deprojection and competing constraints.
+
+The generic photometric compiler separates a smooth MGE envelope from finite
+positive image residuals. The current candidate uses an authored envelope fraction
+of 0.95, an 8-fit-pixel smoothing scale, and a maximum of 4096 residual features.
+Each residual receives one conditional depth and finite XYZ material; each optical
+lens supplies coarse envelope chromaticity and component colors on common geometry.
+The selected analytic fit retained 4085 features, with total RMSE 0.03825, 5.90%
+missing light and 17.79% excess light. These display-fit metrics are not a visual
+acceptance or a calibrated photometric validation.
+
+The field represents **integrated starlight**, not resolved member stars. Source
+stellar light is preserved, with zero extracted stellar residual and no separate
+star catalogue layer. The 512-pixel fit grid and minimum projected sigma of 0.9
+fit pixels (5.80 arcsec here) cannot recover native stellar widths or all crowded
+core texture. This is neither measured stellar depth nor a dynamical mass model.
+
+## Source images
+
+| Lens | Native publisher image |
+| --- | --- |
+| [VST/OmegaCAM, eso1119b](https://www.eso.org/public/images/eso1119b/) | Optical G/R/I, 14540 × 14540 pixels, 50.88′ square; light/geometry reference. |
+| [WFI, eso0844a](https://www.eso.org/public/images/eso0844a/) | Optical B/V/I, 8040 × 7560 pixels, 31.88′ × 29.99′; second material lens. Some publisher mosaic gaps contain DSS data. |
+
+Native TIFFs, complete AVM metadata and identities are pinned. Angular registration
+uses native dimensions. Earlier 8192-pixel WebP preparation is historical; it is not
+the current scientific source. ESO credits and reuse terms are retained in the
+[observation recipe](observations.json): VST credit is ESO/INAF-VST/OmegaCAM,
+with acknowledgement to A. Grado and L. Limatola/INAF-Capodimonte Observatory;
+WFI credit is ESO. Both follow the [ESO image-use terms](https://www.eso.org/public/outreach/copyright/).
+
+## Native registration
+
+The [current native receipt](native-registration.json) passes the unchanged field-star
+gate with **1,899 automatic VST/WFI correspondences, 633 held-out stars and 0.0972 arcsec
+held-out RMS**. All four spatial quadrants are covered; the held-out stars span over 99.7%
+of the common image footprint in each direction. This validates relative registration,
+not absolute sky coordinates, stellar membership, depth or photometry. The publisher says
+some WFI mosaic gaps use DSS data; those regions are not independently attributed detections.
+
+The earlier recipe incorrectly put each AVM reference coordinate at the raster centre.
+Both native TIFFs actually carry noncentral reference pixels in a smaller AVM reference
+grid. Keeping those complete embedded WCS records fixes the roughly 100-arcsec relative
+offset without a fitted calibration or relaxed gate. The source pins, exact AVM values,
+gate metrics and failing recentred/corrupted-identity controls are in the receipt.
+`verify-registration.mts` checks the embedded metadata against the processing recipe and
+replays the fit from the prepared native source cache. A small spatially distributed
+native-star fixture tests the same defect without requiring the large images in CI.
+
+[The original pointing check](registration.json) and [DSS trial](dss2-fits-registration-report.json)
+are preserved historical results from the incorrect metadata transcription. The former's
+0.24-arcsec centre comparison is roughly one native pixel, despite its original sub-pixel
+wording. Neither historical receipt qualifies the corrected native inputs.
+
+## Actual results and current blocker
+
+The current inspection uses **459 retained slabs**, with one neutral bank and two
+optical lenses. [Front, oblique and both side views](evidence/2026-09-20/README.md)
+show the actual retained-DOM compiler renderer. The inspection wrapper is not the
+main application or a promoted Lab delivery. The object remains a research subject;
+no unfinished application package or new runtime bank is shipped here.
+
+- **Bake 1:** `41d62d…c3e962` completed but was visually rejected for large bright
+ clumps from the finite supports.
+- **Bake 2:** `412d07…1fa7bdca` stopped at 66% during VST painting; the
+ [original receipt](evidence/2026-09-20/bake2-compile.json) records the failure.
+ A valid convex color mixture produced `255.00000000000003`. The correction
+ rejects nonfinite values before bounding finite roundoff; its regression and
+ 32,173,980 retained samples per lens pass. This does not qualify the renderer.
+- **Bake 3:** `5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9`
+ completed both lenses in 271.59 seconds for local inspection. The
+ [compile receipt](evidence/2026-09-20/bake3-compile.json) and
+ [Lab browser report](evidence/2026-09-20/bake3-browser.json) identify that state.
+ All 1,040 neutral PNGs and geometry match bake 2, as recorded by the
+ [complete identity comparison](evidence/2026-09-20/bake3-identity.json).
+ Its failed [handoff measurements](evidence/2026-09-20/legacy-handoffs.json)
+ therefore remain applicable. Historical screenshot paths inside that report
+ are local archival paths; the four current images are retained beside it.
+
+| Interpolation | X/Z normalized L1 | Y/Z normalized L1 | Fixed limit |
+| --- | ---: | ---: | ---: |
+| Smooth, original bank | 0.105794 | 0.094072 | 0.04 |
+| Nearest, original bank | 0.096259 | 0.082798 | 0.04 |
+| Smooth, adaptive bank | 0.124987 | 0.114655 | 0.04 |
+
+These checks ran on the working tree based on
+`ef1e8b067f8f7d23d4bf13ea065f02f1dec7f4ad` plus the registration, photometric
+compiler, compact replay, coordinate transport and adaptive-layer changes now
+published in this branch. That base commit alone is not the tested version.
+The [validation record](evidence/2026-09-20/validation.json) pins the tested sources
+and records reused checks, local failures and omitted application qualification.
+
+## Adaptive-layer experiment — unaccepted
+
+New compiler bakes plan at most **500 total XYZ slabs** while integrating every
+reference depth sample. This experiment reused bake 3's pinned field and both
+materials without source acquisition or refitting. Its inspection identity is
+`7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549`.
+The [bake receipt](evidence/2026-09-20/layer-bake.json) records 461 planned slabs
+(147/143/171), 459 retained slabs (146/142/171), and 230.94 seconds for the bake.
+That is **41.1% fewer retained planes**, with approximately 5–7% fewer texture bytes.
+It is not a corresponding bake-speed claim: depth sampling work is preserved.
+
+The [browser comparison](evidence/2026-09-20/layer-handoffs.json) uses the same
+renderer, camera and interpolation for original and optimized banks. Front-view
+normalized L1 stays below 0.0065 and luminance changes by about 1.2%. Both optical
+lenses and neutral still fail the unchanged 0.04 X/Z and Y/Z image-handoff limit.
+The additional X/Y diagnostic is recorded too; its old neutral outlier is not used
+as a stable comparison. The planner's displacement estimate is not rendered-quality
+acceptance.
+
+The [numerical alignment probe](evidence/2026-09-20/numerical-alignment.json)
+fits translation, scale and rotation. It reduces L1 by only 2.6–5%; all twelve
+old/new X/Z and Y/Z pairs remain failed. Native relative registration separately
+passes at 0.097175 arcsec held-out RMS. These results do not support a simple global
+2D misregistration as the explanation; they do not exclude sampling or compositing
+defects. [Luminosity-weighted shape moments](evidence/2026-09-20/shape-moments.json)
+in the retained envelope domain give approximate principal-axis ratios
+1 : 0.98 : 0.83; this is not an observed isophotal shape measurement or side-view
+rendering acceptance.
+
+The independent [Crab replay comparison](evidence/2026-09-20/crab-comparison.json)
+reduces 542 retained slabs to 391 and verifies all 2,737 texture pins and decoded
+alpha/geometry. Twelve of fourteen handoffs pass before and after. The two Spitzer
+failures persist and worsen, so this experiment does not promote Crab either.
+
+Main-page delivery, cold compact replay, R2 publication/restoration, source-to-app
+browser conformance and visual acceptance remain pending. The actual Lab publication
+is still bake 3; the adaptive specimen is a separately identified inspection.
+
+## Historical spherical King-Abel attempt — rejected
+
+[king-abel-profile.json](king-abel-profile.json) and
+[king-abel-derivation.mts](king-abel-derivation.mts) preserve the earlier reasoning;
+they are **not the selected prior**. That experiment adopted a King (1962) projected
+profile, substituted a 4.54 pc three-dimensional dynamical core radius for its
+projected core radius, and approximated projected half-light radius as
+0.75 × 10.42 = 7.815 pc from a three-dimensional half-mass radius. Solving those
+constraints gave rt/rc ≈ 10.221 and rt ≈ 46.4 pc before numerical Abel inversion.
+The 201-point table's recorded quadrature convergence established numerical
+consistency of that assumed profile, not an observed photometric fit.
+
+Its two radius substitutions and spherical symmetry were insufficiently sourced
+for the desired reconstruction. Calling it a published-model consequence did not
+make those substitutions measured. Later primary-source intake supplied the MGE
+table and a Wilson alternative; [shape evidence](shape-evidence.md) retains the
+comparison, radius-dependent flattening and inclination limits.
+
+The original tooling note also described a genuine historical gap: the existing
+simulation workflow required a particle-simulation baseline, while shell and
+pulsar-wind recipes encoded unrelated physics. That gap motivated the generic
+photometric-prior compiler now used above. No fabricated N-body baseline was added.
+The current blocker is the measured renderer handoff failure, not that old tooling
+gap, missing subject registration, or an absence of attempted bakes.
diff --git a/labs/nebula/models/omega-centauri/candidates.json b/labs/nebula/models/omega-centauri/candidates.json
new file mode 100644
index 0000000000..1fd64c2416
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/candidates.json
@@ -0,0 +1,6 @@
+{
+ "schema": "cssearth-nebula-candidates@1",
+ "candidates": [
+ { "id": "omega-centauri", "compilerRecipe": "labs/nebula/models/omega-centauri/compiler.json" }
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/compiler.json b/labs/nebula/models/omega-centauri/compiler.json
new file mode 100644
index 0000000000..1acd5bdc90
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/compiler.json
@@ -0,0 +1,15 @@
+{
+ "schema": "cssearth-nebula-compiler@1",
+ "id": "omega-centauri",
+ "label": "Omega Centauri",
+ "observationRecipe": "labs/nebula/models/omega-centauri/observations.json",
+ "observationCatalogue": ".local/nebula-lab/observations/omega-centauri/observations.json",
+ "structureRecipe": "labs/nebula/models/omega-centauri/observation-structures.json",
+ "structureCatalogue": ".local/nebula-lab/observations/omega-centauri/structures/catalogue.json",
+ "photometricPriorRecipe": "labs/nebula/models/omega-centauri/photometric-mge.json",
+ "defaultSourceId": "eso1119b",
+ "sourceWeights": { "eso1119b": 1, "eso0844a": 0 },
+ "defaultControls": { "detail": 1, "faint": 0.35, "depth": 1 },
+ "maximumStars": 0,
+ "interpretation": "The published oblate eight-Gaussian surface-brightness model supplies a conditional three-dimensional starlight prior at its selected inclination and position angle. Registered VST light fits finite emission components within that prior; the independent WFI image recolours the same geometry. Individual feature depths, finite supports and near/far tilt are conditional display choices, not measured stellar positions or membership. Integrated stellar light is preserved; no duplicate compact-star catalogue is added. This is relative display emission, not calibrated luminosity, gas density or a recovered dynamical model."
+}
diff --git a/labs/nebula/models/omega-centauri/dss2-fits-registration-report.json b/labs/nebula/models/omega-centauri/dss2-fits-registration-report.json
new file mode 100644
index 0000000000..02eec85458
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/dss2-fits-registration-report.json
@@ -0,0 +1,132 @@
+{
+ "schema": "cssearth-image-alignment-report@1",
+ "pass": false,
+ "sources": [
+ {
+ "id": "eso1119b",
+ "pass": false,
+ "sourcePath": ".local/nebula-lab/source-originals/eso1119b.tif",
+ "sourceSha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "downsampledTo": [
+ 3053,
+ 3053
+ ],
+ "frameWidth": 225,
+ "geometry": {
+ "kind": "matched-star-homography-vs-dss2-fits",
+ "matrix": [
+ 0.06270687861416066,
+ -0.00009665152495055059,
+ -0.00013531657182341058,
+ 0.062421519784384205,
+ 17.055830202431903,
+ 17.36069174483188
+ ]
+ },
+ "evidence": {
+ "status": "publisher",
+ "matchedStars": 2588,
+ "trainingStars": 646,
+ "trainingCandidates": 1725,
+ "rejectedTrainingStars": 1079,
+ "heldOutStars": 863,
+ "rmsPixels": 0.9754047610820139,
+ "maxResidualPixels": 2.551939937889531,
+ "spatialQuadrants": 4,
+ "coverageFraction": [
+ 0.9897439576980328,
+ 0.9942620833466778
+ ],
+ "relativeScale": 1.0010884923866699,
+ "centreShiftPixels": 0.05062707161605429,
+ "coverageDomain": {
+ "kind": "common-observed-footprint",
+ "sourcePixelPolygon": [
+ [
+ 3053,
+ -1.4210854715202004e-14
+ ],
+ [
+ 3053,
+ 3053
+ ],
+ [
+ 0,
+ 3053
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ },
+ "residualArcseconds": 15.60647617731222,
+ "interpretation": "Relative field-star alignment to the configured reference image; absolute sky scale and orientation remain publisher metadata. No stellar membership or physical depth.",
+ "matchTableNote": "Per-pair table omitted: 2588 pairs, reproducible by re-running labs/nebula/models/omega-centauri/verify-against-dss2-fits.mts against the pinned sources."
+ }
+ },
+ {
+ "id": "eso0844a",
+ "pass": false,
+ "sourcePath": ".local/nebula-lab/source-originals/eso0844a.tif",
+ "sourceSha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429",
+ "downsampledTo": [
+ 1913,
+ 1799
+ ],
+ "frameWidth": 225,
+ "geometry": {
+ "kind": "matched-star-homography-vs-dss2-fits",
+ "matrix": [
+ 0.06242175929935033,
+ -0.00004287637872192954,
+ 0.00003435046586434953,
+ 0.06256983723152591,
+ 52.751600247196286,
+ 56.334478341107676
+ ]
+ },
+ "evidence": {
+ "status": "publisher",
+ "matchedStars": 1507,
+ "trainingStars": 606,
+ "trainingCandidates": 1004,
+ "rejectedTrainingStars": 398,
+ "heldOutStars": 503,
+ "rmsPixels": 0.6548057862204336,
+ "maxResidualPixels": 1.9186522832899346,
+ "spatialQuadrants": 4,
+ "coverageFraction": [
+ 0.9941922186026076,
+ 0.9926392826751006
+ ],
+ "relativeScale": 0.9998733807165251,
+ "centreShiftPixels": 0.07457734644866301,
+ "coverageDomain": {
+ "kind": "common-observed-footprint",
+ "sourcePixelPolygon": [
+ [
+ 1913,
+ 0
+ ],
+ [
+ 1913,
+ 1799
+ ],
+ [
+ 0,
+ 1799
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ },
+ "residualArcseconds": 10.476892579526936,
+ "interpretation": "Relative field-star alignment to the configured reference image; absolute sky scale and orientation remain publisher metadata. No stellar membership or physical depth.",
+ "matchTableNote": "Per-pair table omitted: 1507 pairs, reproducible by re-running labs/nebula/models/omega-centauri/verify-against-dss2-fits.mts against the pinned sources."
+ }
+ }
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/dss2-registration-report.json b/labs/nebula/models/omega-centauri/dss2-registration-report.json
new file mode 100644
index 0000000000..ec231cdcfe
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/dss2-registration-report.json
@@ -0,0 +1,37 @@
+{
+ "schema": "cssearth-image-alignment-report@1",
+ "pass": false,
+ "catalogue": "labs/nebula/models/omega-centauri/observations.json",
+ "sources": [
+ {
+ "id": "eso1119b",
+ "pass": false,
+ "sourcePath": ".local/nebula-lab/source-originals/eso1119b.tif",
+ "sourceSha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "geometry": {
+ "kind": "matched-star-homography",
+ "registration": null
+ },
+ "evidence": {
+ "status": "publisher",
+ "matchedStars": 0,
+ "interpretation": "Fewer than 45 matched pairs against the DSS2 reference."
+ }
+ },
+ {
+ "id": "eso0844a",
+ "pass": false,
+ "sourcePath": ".local/nebula-lab/source-originals/eso0844a.tif",
+ "sourceSha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429",
+ "geometry": {
+ "kind": "matched-star-homography",
+ "registration": null
+ },
+ "evidence": {
+ "status": "publisher",
+ "matchedStars": 0,
+ "interpretation": "Fewer than 45 matched pairs against the DSS2 reference."
+ }
+ }
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/README.md b/labs/nebula/models/omega-centauri/evidence/2026-09-20/README.md
new file mode 100644
index 0000000000..2b9dbedb0b
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/README.md
@@ -0,0 +1,44 @@
+# Omega Centauri: 459-layer inspection
+
+These are four browser captures of the same retained 3D field, using the actual
+compiler viewer and PolyCSS renderer in a local inspection wrapper. They are not
+source photographs, main-page captures, or evidence of an accepted delivery.
+The [capture record](captures.json) pins the result, served inputs, images, camera,
+browser version, viewport (1100 × 820) and DPR (1). No image processing was applied.
+
+| Earth view: yaw 0°, pitch 0° | Oblique: yaw 35°, pitch 25° |
+| --- | --- |
+|  |  |
+
+| X side: yaw 90°, pitch 0° | Near Y side: yaw 0°, pitch 89° |
+| --- | --- |
+|  |  |
+
+All views use VST/OmegaCAM colors, zoom 1 and zero pan. The interactive pitch limit
+is 89°, so the final view is one degree short of the exact side. The visible field
+represents integrated starlight with modeled depth: 4,085 fitted light features,
+459 retained logical slabs, and no separate measured-star overlay.
+
+Color source: **ESO/INAF-VST/OmegaCAM. Acknowledgement: A. Grado and
+L. Limatola/INAF-Capodimonte Observatory.** See the
+[native observation and credit](../../observations.json) and
+[ESO image-use terms](https://www.eso.org/public/outreach/copyright/).
+The second available WFI lens is credited to ESO; it is not pictured here.
+
+These views illustrate different orientations, not a matched visual comparison.
+No Pixelmatch result is claimed. The unchanged quantitative handoff gates remain
+failed: [original browser measurements](layer-handoffs.json),
+[bounded numerical alignment](numerical-alignment.json), and
+[retained-input bake receipt](layer-bake.json). The grid and angle-dependent
+appearance still need resolution. The images do not certify rotation stability.
+
+The [validation record](validation.json) identifies the tested source hashes,
+reused checks, five full-suite failures and omitted app/R2 qualification. Its source
+hashes describe the implementation captured here; publishing documentation and
+evidence does not change that implementation. Historical receipts preserve their
+original local paths and result identities; those paths are not public downloads.
+
+The four PNGs total 849,034 bytes and support the four-view inspection claim.
+The 198,200-byte [bake-3 identity report](bake3-identity.json) retains all 1,040
+neutral texture comparisons needed to reuse the earlier failed handoff verdict.
+No runtime slice bank or native observation bytes are committed with these images.
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake2-compile.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake2-compile.json
new file mode 100644
index 0000000000..639b7fb457
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake2-compile.json
@@ -0,0 +1,21 @@
+{
+ "schema": "cssearth-nebula-candidate-run@1",
+ "startedAt": "2026-09-20T09:23:03.906Z",
+ "updatedAt": "2026-09-20T09:26:09.633Z",
+ "catalogue": {
+ "path": "labs/nebula/models/omega-centauri/candidates.json",
+ "sha256": "be78206e67c31d36698c69818a383e922c48603cb157bbd31e8bcf824bb24240"
+ },
+ "mode": "compile",
+ "selected": [
+ "omega-centauri"
+ ],
+ "results": [
+ {
+ "id": "omega-centauri",
+ "status": "failed",
+ "error": "Compiler 3D material must return finite RGB chromaticity in 0..255.",
+ "seconds": 185.725669875
+ }
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-browser.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-browser.json
new file mode 100644
index 0000000000..053f03e4bb
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-browser.json
@@ -0,0 +1,236 @@
+{
+ "id": "5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9",
+ "url": "http://127.0.0.1:4339/reconstruction?subject=omega-centauri&inspection=compiler",
+ "captures": [
+ {
+ "name": "vst-front",
+ "pose": "0,0",
+ "lens": "eso1119b",
+ "mode": "textured",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ },
+ {
+ "name": "vst-oblique",
+ "pose": "44.99999389648437,25.000006103515624",
+ "lens": "eso1119b",
+ "mode": "textured",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "visible",
+ "opacity": "0.5",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ },
+ {
+ "name": "vst-x-positive",
+ "pose": "90.00000915527343,0",
+ "lens": "eso1119b",
+ "mode": "textured",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ },
+ {
+ "name": "vst-x-negative",
+ "pose": "-89.99999847412109,0",
+ "lens": "eso1119b",
+ "mode": "textured",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ },
+ {
+ "name": "vst-y-positive",
+ "pose": "0,88.99999694824218",
+ "lens": "eso1119b",
+ "mode": "textured",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ },
+ {
+ "name": "vst-y-negative",
+ "pose": "0,-88.99999694824218",
+ "lens": "eso1119b",
+ "mode": "textured",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ },
+ {
+ "name": "wfi-front",
+ "pose": "0,0",
+ "lens": "eso0844a",
+ "mode": "textured",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ },
+ {
+ "name": "wfi-oblique",
+ "pose": "44.99999389648437,25.000006103515624",
+ "lens": "eso0844a",
+ "mode": "textured",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "visible",
+ "opacity": "0.5",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ },
+ {
+ "name": "neutral-oblique",
+ "pose": "44.99999389648437,25.000006103515624",
+ "lens": "eso0844a",
+ "mode": "neutral",
+ "original": "false",
+ "bankStates": [
+ {
+ "visibility": "visible",
+ "opacity": "1",
+ "leaves": 615
+ },
+ {
+ "visibility": "hidden",
+ "opacity": "0",
+ "leaves": 615
+ },
+ {
+ "visibility": "visible",
+ "opacity": "0.5",
+ "leaves": 1107
+ }
+ ],
+ "retained": true
+ }
+ ],
+ "errors": [],
+ "failed": [],
+ "posts": [],
+ "refreshVerified": true,
+ "status": "inspection verified; visual handoff UNACCEPTED"
+}
\ No newline at end of file
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-compile.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-compile.json
new file mode 100644
index 0000000000..7f592efce1
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-compile.json
@@ -0,0 +1,33 @@
+{
+ "schema": "cssearth-nebula-candidate-run@1",
+ "startedAt": "2026-09-20T09:44:55.121Z",
+ "updatedAt": "2026-09-20T09:49:26.736Z",
+ "catalogue": {
+ "path": "labs/nebula/models/omega-centauri/candidates.json",
+ "sha256": "be78206e67c31d36698c69818a383e922c48603cb157bbd31e8bcf824bb24240"
+ },
+ "mode": "compile",
+ "selected": [
+ "omega-centauri"
+ ],
+ "results": [
+ {
+ "id": "omega-centauri",
+ "status": "complete",
+ "result": {
+ "path": ".local/nebula-lab/compiler/5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9/result.json",
+ "sha256": "0839b8149a37b7ca2b4d54434fd5881a8e2e27eb0da451e5d028e3a0637b0f4a"
+ },
+ "metrics": {
+ "components": 4085,
+ "unconstrainedComponents": 4085,
+ "stars": 0,
+ "fitRmse": 0.038251625699600056,
+ "baselineRmse": 0.24435542214110098,
+ "missingSignalFraction": 0.05902815700043809,
+ "excessSignalFraction": 0.17786660259727755
+ },
+ "seconds": 271.589149292
+ }
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-identity.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-identity.json
new file mode 100644
index 0000000000..e7a0636284
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/bake3-identity.json
@@ -0,0 +1,7302 @@
+{
+ "previous": "412d070141ce5119bd773d7a1eec65f12f78fa36ced3f930aa4fc9491fa7bdca",
+ "current": "5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9",
+ "identical": true,
+ "imageCount": 1040,
+ "preparedGeometry": [
+ "frame",
+ "anchors",
+ "stacks",
+ "resources",
+ "approximation"
+ ],
+ "sourceGeometry": [
+ "quads",
+ "boundsUnits",
+ "approximation"
+ ],
+ "resources": [
+ {
+ "path": "slices/x/00.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/01.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/02.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/03.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/04.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/05.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/06.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/07.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/08.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/09.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/10.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/100.png",
+ "bytes": 14849,
+ "sha256": "f3cd456668aace6836374a0d3cae22ac853494c4f42e3d958d6b4a94419c9b99",
+ "width": 357,
+ "height": 413
+ },
+ {
+ "path": "slices/x/101.png",
+ "bytes": 15595,
+ "sha256": "d1402e6b8c0ad2e32f3a4cf474c46f8304d14dd765a56c37ec1b04d41fd57b4e",
+ "width": 358,
+ "height": 579
+ },
+ {
+ "path": "slices/x/102.png",
+ "bytes": 15782,
+ "sha256": "d3c03b6ac95c34c3c48dd07f1d153a9c6641ff97670fc6986e76839b408894fd",
+ "width": 358,
+ "height": 610
+ },
+ {
+ "path": "slices/x/103.png",
+ "bytes": 16134,
+ "sha256": "4e7999e49da1192756534e6ce0543ad8d9db1b550a96dd279f3c0f8a9c0ba4ad",
+ "width": 359,
+ "height": 594
+ },
+ {
+ "path": "slices/x/104.png",
+ "bytes": 15805,
+ "sha256": "8e93454ca9d418ccd344e4e23def5b1290a73f32e1c7a075fa416de3093f4bb6",
+ "width": 358,
+ "height": 423
+ },
+ {
+ "path": "slices/x/105.png",
+ "bytes": 15823,
+ "sha256": "11e73c53996efb610b74c26ccb020a3490c3457ab94d6fe2f55b575544fc4083",
+ "width": 360,
+ "height": 415
+ },
+ {
+ "path": "slices/x/106.png",
+ "bytes": 16171,
+ "sha256": "da6e6104523d8202e6dfee30b8f699c3bca6ca47c51345e23059950d52ca6faa",
+ "width": 360,
+ "height": 433
+ },
+ {
+ "path": "slices/x/107.png",
+ "bytes": 16373,
+ "sha256": "9037e675ba1fc3363d978dc85d93879818daa72f7ce6111c72e67a97661d0b2c",
+ "width": 360,
+ "height": 432
+ },
+ {
+ "path": "slices/x/108.png",
+ "bytes": 16036,
+ "sha256": "b8b75327c0d9f4500c657770e6e6aa130ad3b98b4daa71254ae5edadf3af84c7",
+ "width": 358,
+ "height": 407
+ },
+ {
+ "path": "slices/x/109.png",
+ "bytes": 16896,
+ "sha256": "8de735439ad2082f548943ba7396fdda9ef56448ab18e5f77928deb8e360c40a",
+ "width": 359,
+ "height": 599
+ },
+ {
+ "path": "slices/x/11.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/110.png",
+ "bytes": 16991,
+ "sha256": "271c6e9d6092d6ef64906b9fd3fc9276a4e9faa06e55cf8fce9d630591c66279",
+ "width": 361,
+ "height": 599
+ },
+ {
+ "path": "slices/x/111.png",
+ "bytes": 16586,
+ "sha256": "746d9f4cdced0c71b4e97ef46360b784f856898540855e4337ff2f9e486066c1",
+ "width": 357,
+ "height": 465
+ },
+ {
+ "path": "slices/x/112.png",
+ "bytes": 17197,
+ "sha256": "79adf8fb7e595add12651a6a12969c004a233fa8a3d50fb01d1bf58eaf0e8272",
+ "width": 361,
+ "height": 524
+ },
+ {
+ "path": "slices/x/113.png",
+ "bytes": 17430,
+ "sha256": "06fc41f772c42be56c9ba3c1c3162674ab3421137d4193775a1195faa53574c1",
+ "width": 363,
+ "height": 523
+ },
+ {
+ "path": "slices/x/114.png",
+ "bytes": 17068,
+ "sha256": "e4bef6d1a4ec117688a5d0b7e332c29181c53bada82fb43013bf2812eb1074f1",
+ "width": 363,
+ "height": 467
+ },
+ {
+ "path": "slices/x/115.png",
+ "bytes": 17363,
+ "sha256": "5887ad6c674fe45b3780842e144cf3fff7811f00016960efb79c5fbf0c37ded3",
+ "width": 366,
+ "height": 543
+ },
+ {
+ "path": "slices/x/116.png",
+ "bytes": 17847,
+ "sha256": "cd483f3a038ee96d88c81f441caf122e67c40940a5e43ee822a5e8505c83f85e",
+ "width": 366,
+ "height": 545
+ },
+ {
+ "path": "slices/x/117.png",
+ "bytes": 18056,
+ "sha256": "24d632b49dc86d9c9876c039ba9afcc1f30fa9e4a477a3a0a6267cc011c33334",
+ "width": 367,
+ "height": 545
+ },
+ {
+ "path": "slices/x/118.png",
+ "bytes": 17862,
+ "sha256": "f23d61a31f8efbf4b1e1ff153fa9e26a7fe92c7aecc3fde0ea02830b7f8834cc",
+ "width": 370,
+ "height": 495
+ },
+ {
+ "path": "slices/x/119.png",
+ "bytes": 18975,
+ "sha256": "989fd4b0b61c8982eebedad8ae8ca4697ead5117dc3410d5ada226b1119780dd",
+ "width": 370,
+ "height": 787
+ },
+ {
+ "path": "slices/x/12.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/120.png",
+ "bytes": 18901,
+ "sha256": "1f3f2605e356b9a3ca94593fe39dc5b45c6cd35ea8618d79b45ea1de2d236371",
+ "width": 369,
+ "height": 768
+ },
+ {
+ "path": "slices/x/121.png",
+ "bytes": 18428,
+ "sha256": "c531cfc8a559e4e023b6ed13269a8d5e159d5a01d64d10c544ce928f47a05cce",
+ "width": 371,
+ "height": 573
+ },
+ {
+ "path": "slices/x/122.png",
+ "bytes": 18280,
+ "sha256": "b45759c0933b6b90e2d05fefd45aae080bd425acaabf3d3cf7e87c592fc6e582",
+ "width": 371,
+ "height": 495
+ },
+ {
+ "path": "slices/x/123.png",
+ "bytes": 18539,
+ "sha256": "c757f816c2596346c5ff26fe18a87bc39f055f2a5343d171e42d90fa49b11457",
+ "width": 373,
+ "height": 579
+ },
+ {
+ "path": "slices/x/124.png",
+ "bytes": 18520,
+ "sha256": "22e01e22d9b622d09981a3f604891a9d1fa741fcf2fa722295b799995f79ac35",
+ "width": 373,
+ "height": 581
+ },
+ {
+ "path": "slices/x/125.png",
+ "bytes": 18311,
+ "sha256": "fb7ceb99b441e2bb2e3e45950cc5906c9ac00a931243c672c847381946d824bd",
+ "width": 370,
+ "height": 507
+ },
+ {
+ "path": "slices/x/126.png",
+ "bytes": 18300,
+ "sha256": "93688d5a192a896b15bc871ccb6858189aa59292ce5b9b6fe9b79cdb3f9a5d2a",
+ "width": 373,
+ "height": 506
+ },
+ {
+ "path": "slices/x/127.png",
+ "bytes": 18341,
+ "sha256": "323d3b4c1007cad11ad3a04a32c37ef2217b19df42c4eee76f68c934bcdc4d2e",
+ "width": 373,
+ "height": 503
+ },
+ {
+ "path": "slices/x/128.png",
+ "bytes": 18234,
+ "sha256": "e2d3bd8c58005a602eb0b429c25e077bbde8b94a1062c932f5a667d9398a36d1",
+ "width": 372,
+ "height": 502
+ },
+ {
+ "path": "slices/x/129.png",
+ "bytes": 18392,
+ "sha256": "880e6e506b11cbd5559a6be9f7dcfe7e0bf3d8903c6d30aa8397be91d5623ef8",
+ "width": 372,
+ "height": 510
+ },
+ {
+ "path": "slices/x/13.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/130.png",
+ "bytes": 18571,
+ "sha256": "67a7683416978920256e406aaaa64d1fecef9158606a619dc3197e134ddf773a",
+ "width": 373,
+ "height": 510
+ },
+ {
+ "path": "slices/x/131.png",
+ "bytes": 18483,
+ "sha256": "3c1022a5fc82af7beaf3c1d8afadcf6ef40e11f49f2b7c05b5d512a15957ece7",
+ "width": 361,
+ "height": 480
+ },
+ {
+ "path": "slices/x/132.png",
+ "bytes": 18575,
+ "sha256": "879bf3984f4f9fcd6c959bfc7cc77742b23e1cd563ef6ec41d262fece6ea67db",
+ "width": 364,
+ "height": 479
+ },
+ {
+ "path": "slices/x/133.png",
+ "bytes": 18449,
+ "sha256": "35044a81ecd9e55a9ceba93aeb257d4c0978a867b0b47db98a8457c452795ad5",
+ "width": 376,
+ "height": 505
+ },
+ {
+ "path": "slices/x/134.png",
+ "bytes": 18371,
+ "sha256": "c8e9afb344bfc1e224e8d49fd6b663205865c8159967cc476f6bbf0390243fb7",
+ "width": 373,
+ "height": 477
+ },
+ {
+ "path": "slices/x/135.png",
+ "bytes": 18511,
+ "sha256": "9063b2d27cd79411256ce3f19f229b10b44dd183ba9dd5b5a4d043030ea2c1ca",
+ "width": 363,
+ "height": 480
+ },
+ {
+ "path": "slices/x/136.png",
+ "bytes": 18272,
+ "sha256": "864a92201b3b58a13133af8f247885cdd01914b564e02f013ba6575015bb780e",
+ "width": 363,
+ "height": 441
+ },
+ {
+ "path": "slices/x/137.png",
+ "bytes": 18257,
+ "sha256": "e9fdd8bb1670649ba3bb8e746d32a020bf3df461162e4641e9b737a412baced4",
+ "width": 362,
+ "height": 478
+ },
+ {
+ "path": "slices/x/138.png",
+ "bytes": 18181,
+ "sha256": "0799790d652bd8cb347dd750accd6f279d398f335a5c9c680d44fbdec6509648",
+ "width": 363,
+ "height": 484
+ },
+ {
+ "path": "slices/x/139.png",
+ "bytes": 17849,
+ "sha256": "7d78538ba674810118730b71553b94b7edb8bbfa029ba402395106d089d41161",
+ "width": 364,
+ "height": 382
+ },
+ {
+ "path": "slices/x/14.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/140.png",
+ "bytes": 18190,
+ "sha256": "2120a17f258c7b14c83ff266a953ac6b59bd8281b16b3df515ca46861f5bca9e",
+ "width": 362,
+ "height": 459
+ },
+ {
+ "path": "slices/x/141.png",
+ "bytes": 17893,
+ "sha256": "4977a93244c0f92b8b8dcc00d65343180edd9c9b7bfa8007a68bd2340bbd0558",
+ "width": 363,
+ "height": 396
+ },
+ {
+ "path": "slices/x/142.png",
+ "bytes": 18166,
+ "sha256": "185f23cb5022ba20602c4da969d0c2e49d71672c998300a7619a83bcbb909b70",
+ "width": 364,
+ "height": 481
+ },
+ {
+ "path": "slices/x/143.png",
+ "bytes": 18467,
+ "sha256": "702f991b243853a3409a95f7d1969a2eb1ebb20555f558cdf40ac7867ff76e2c",
+ "width": 365,
+ "height": 518
+ },
+ {
+ "path": "slices/x/144.png",
+ "bytes": 18336,
+ "sha256": "fe0bd2b3acb0d7f7add5bfa229a438fe6a1d7b910483b035e53a556b2638345f",
+ "width": 364,
+ "height": 448
+ },
+ {
+ "path": "slices/x/145.png",
+ "bytes": 18419,
+ "sha256": "994238c67731c24e256badb07891e2732c7dee5fbce3c6ffdce745314fec0cfb",
+ "width": 364,
+ "height": 485
+ },
+ {
+ "path": "slices/x/146.png",
+ "bytes": 18457,
+ "sha256": "b55d184d9c7e1ae80e77d300e121b94c7e3c91320658a9c748d9c70fded6c9c8",
+ "width": 366,
+ "height": 467
+ },
+ {
+ "path": "slices/x/147.png",
+ "bytes": 18028,
+ "sha256": "cd85aebf80438196e371def838be3d30963780f0ba6939a8446ca29ab4d534ad",
+ "width": 365,
+ "height": 400
+ },
+ {
+ "path": "slices/x/148.png",
+ "bytes": 18235,
+ "sha256": "2a32ed63efb6a6f5fe28f0ae2b0a6ef61ce78e1b68293327146c660973b6ea00",
+ "width": 366,
+ "height": 410
+ },
+ {
+ "path": "slices/x/149.png",
+ "bytes": 18251,
+ "sha256": "61860618193f0c545260313292903bd78736729262fc50d01fc2b14313c56807",
+ "width": 378,
+ "height": 411
+ },
+ {
+ "path": "slices/x/15.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/150.png",
+ "bytes": 17913,
+ "sha256": "c837727fc89d706bac3727e9e0f7fd6ee65bd404542c5f9734ec0a391e558479",
+ "width": 377,
+ "height": 410
+ },
+ {
+ "path": "slices/x/151.png",
+ "bytes": 17382,
+ "sha256": "517973fd5d999d76481ae85b770e57f2bb14eb6f4e22910384dff9435ade8b63",
+ "width": 364,
+ "height": 363
+ },
+ {
+ "path": "slices/x/152.png",
+ "bytes": 17563,
+ "sha256": "02eed9395f6a82f33b4499e55527ba2ce76cb4782138d52ee0b1904295c4fb6e",
+ "width": 372,
+ "height": 353
+ },
+ {
+ "path": "slices/x/153.png",
+ "bytes": 17725,
+ "sha256": "6b336460be35dc002a5b209af6ba801306421c542c05ed8aaddcb2069e03c3f8",
+ "width": 374,
+ "height": 421
+ },
+ {
+ "path": "slices/x/154.png",
+ "bytes": 17701,
+ "sha256": "8c1b5cb9a5a273d8c449cdf0a6446d76ef63494ffbff69f06ddd379a7f8df8cf",
+ "width": 368,
+ "height": 431
+ },
+ {
+ "path": "slices/x/155.png",
+ "bytes": 17536,
+ "sha256": "583801dc9b1b5a8bbf0202b18126a5fd394d7198166e96550e98eae8cc6f5626",
+ "width": 364,
+ "height": 432
+ },
+ {
+ "path": "slices/x/156.png",
+ "bytes": 18142,
+ "sha256": "d9bf00115174fb854330ddf255e7ccaa01dc8d879eb0e1a25d8c44916e01a9b1",
+ "width": 369,
+ "height": 527
+ },
+ {
+ "path": "slices/x/157.png",
+ "bytes": 17716,
+ "sha256": "a38a2ea34bb32064d8320b816a8811d2389414fbbe86bf6cf4e3aa8460643179",
+ "width": 368,
+ "height": 432
+ },
+ {
+ "path": "slices/x/158.png",
+ "bytes": 17707,
+ "sha256": "be11508b319ed7bc506a628e860c101def4c261a35520b360cd839153313dfc2",
+ "width": 365,
+ "height": 433
+ },
+ {
+ "path": "slices/x/159.png",
+ "bytes": 17012,
+ "sha256": "78e54d5c142b73da97c4cf12023f75ed6b9036399a869d3729710a03bd555514",
+ "width": 361,
+ "height": 358
+ },
+ {
+ "path": "slices/x/16.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/160.png",
+ "bytes": 16932,
+ "sha256": "22d23b869bfb19cb00a09bf115fd1713dbe3d55624b02a3e70880e72a7f98335",
+ "width": 370,
+ "height": 380
+ },
+ {
+ "path": "slices/x/161.png",
+ "bytes": 17069,
+ "sha256": "c2bac67cc8074ccff00d71d656b707e764c00adc56dafe997afbe17d53899105",
+ "width": 369,
+ "height": 402
+ },
+ {
+ "path": "slices/x/162.png",
+ "bytes": 16725,
+ "sha256": "91d17b2b9bf0ae03955fc617b9c2f662b049c8008ba2147c30fdcb82481c74d7",
+ "width": 369,
+ "height": 367
+ },
+ {
+ "path": "slices/x/163.png",
+ "bytes": 17015,
+ "sha256": "52325bd778b2b8e0bfe2eaab2a39e528c82c834ce6f9805baf1f6d6fe2c96176",
+ "width": 369,
+ "height": 449
+ },
+ {
+ "path": "slices/x/164.png",
+ "bytes": 17808,
+ "sha256": "d10c7bf215a976ec34cf9bc65026a0a7c20dff7a4db100b469c599f517ad452d",
+ "width": 368,
+ "height": 612
+ },
+ {
+ "path": "slices/x/165.png",
+ "bytes": 17432,
+ "sha256": "2e6276d4a767dacfce3e7346d73e79a87e5b49775ee4547c48b99a3863115eb6",
+ "width": 371,
+ "height": 611
+ },
+ {
+ "path": "slices/x/166.png",
+ "bytes": 17373,
+ "sha256": "f0dca34b083415c21fbe965c8b18e529aba5d5d3ecda7fe15125f7f4bc095276",
+ "width": 369,
+ "height": 551
+ },
+ {
+ "path": "slices/x/167.png",
+ "bytes": 16958,
+ "sha256": "36fc7784d4088df87279927a3e01d4f32913e3820414a35f67f00cafaa80b732",
+ "width": 369,
+ "height": 477
+ },
+ {
+ "path": "slices/x/168.png",
+ "bytes": 17731,
+ "sha256": "436fb59a9ab12e2976c0e3b13ec12bd833241b320806b10beed1352b2571580f",
+ "width": 367,
+ "height": 829
+ },
+ {
+ "path": "slices/x/169.png",
+ "bytes": 17637,
+ "sha256": "837341499278117934987d7ecd063cab800c3965922a2a23314b22b948be09db",
+ "width": 366,
+ "height": 829
+ },
+ {
+ "path": "slices/x/17.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/170.png",
+ "bytes": 16688,
+ "sha256": "0701e432debd0e909700b50eb4f43cf6538bebff26531e17030e458a47ef1ce6",
+ "width": 364,
+ "height": 620
+ },
+ {
+ "path": "slices/x/171.png",
+ "bytes": 16311,
+ "sha256": "d0137a350d893b18344602267ad02ea9785e7710f4c2068f759b7846afb318e2",
+ "width": 364,
+ "height": 632
+ },
+ {
+ "path": "slices/x/172.png",
+ "bytes": 16117,
+ "sha256": "7a9c59c9207488a84692993f7498857bfb83785a69ef76f9ff318ed750b66768",
+ "width": 363,
+ "height": 634
+ },
+ {
+ "path": "slices/x/173.png",
+ "bytes": 15752,
+ "sha256": "6ff8d53e91d1bcfdb9a78009efa756219820544e3685db01f21ef68c177333fb",
+ "width": 374,
+ "height": 473
+ },
+ {
+ "path": "slices/x/174.png",
+ "bytes": 15030,
+ "sha256": "ca19a2fe2ff7efff3bfcafa628543c6f805ddefaeb54864109a7bf057b706bae",
+ "width": 367,
+ "height": 361
+ },
+ {
+ "path": "slices/x/175.png",
+ "bytes": 14848,
+ "sha256": "f2a1b5f6c63477c0559ac9f92f20f80da1879ca0f7ea20552852ddfd1cb93ee8",
+ "width": 362,
+ "height": 397
+ },
+ {
+ "path": "slices/x/176.png",
+ "bytes": 14362,
+ "sha256": "f4313b31d4e4da52c162e6f715444892338ccc7ff939a999e0df5deb6afc62bd",
+ "width": 363,
+ "height": 397
+ },
+ {
+ "path": "slices/x/177.png",
+ "bytes": 14349,
+ "sha256": "e12ce30dd9a61133dc420653400871ab2c752bcee6b7bf6f9207eb1ba932e1f3",
+ "width": 367,
+ "height": 409
+ },
+ {
+ "path": "slices/x/178.png",
+ "bytes": 14298,
+ "sha256": "a1fb7acede8f7d9fbd74e58a6bf1593e2f86a0565db1861383c7a60b5ed067aa",
+ "width": 369,
+ "height": 415
+ },
+ {
+ "path": "slices/x/179.png",
+ "bytes": 14362,
+ "sha256": "fd3e0410d453117f07892d5ed49fa7935f90736dce226380a7a61a0f72d1a852",
+ "width": 368,
+ "height": 410
+ },
+ {
+ "path": "slices/x/18.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/180.png",
+ "bytes": 13921,
+ "sha256": "ffe777888891a454629b61bfd58c631514175a8a1f84ea1293d82b1e31d15aea",
+ "width": 372,
+ "height": 417
+ },
+ {
+ "path": "slices/x/181.png",
+ "bytes": 13608,
+ "sha256": "6a657e6a43610ed995ef0054ab23b236988a1bdf76fcac8313b20c08a32f8500",
+ "width": 369,
+ "height": 417
+ },
+ {
+ "path": "slices/x/182.png",
+ "bytes": 13395,
+ "sha256": "5136fcfec8d6fb23648895de40e332fc06264228126005e82e04bf20c6877bdd",
+ "width": 373,
+ "height": 417
+ },
+ {
+ "path": "slices/x/183.png",
+ "bytes": 12646,
+ "sha256": "ffd8e2a7adcc7544cf8b5a3277f7760ce0ed9cdad6276387bcc85627039fa0a0",
+ "width": 357,
+ "height": 375
+ },
+ {
+ "path": "slices/x/184.png",
+ "bytes": 12422,
+ "sha256": "d3fa8122b606666128f6cc20b1b909201f201ca4278060bd980a7248fc338f13",
+ "width": 372,
+ "height": 396
+ },
+ {
+ "path": "slices/x/185.png",
+ "bytes": 12294,
+ "sha256": "fbca2f4223eafdac1f177a8c580825eb206ecf4759cdabb909b925cf1c728392",
+ "width": 362,
+ "height": 492
+ },
+ {
+ "path": "slices/x/186.png",
+ "bytes": 11530,
+ "sha256": "008f4ea34f8b138edf54edfe3455ee2f285c3b3e6ab1a4a7a23c1f7011b453cf",
+ "width": 359,
+ "height": 493
+ },
+ {
+ "path": "slices/x/187.png",
+ "bytes": 10519,
+ "sha256": "ee81aeaf5efe69fd92183b0d21a385526cae3f420acfe1422e5a3b73e391391c",
+ "width": 355,
+ "height": 364
+ },
+ {
+ "path": "slices/x/188.png",
+ "bytes": 10613,
+ "sha256": "36f496aa57dbbed701fcd5582022252337ea9e7093e514d830ef952af9eadd8e",
+ "width": 345,
+ "height": 431
+ },
+ {
+ "path": "slices/x/189.png",
+ "bytes": 11038,
+ "sha256": "582df28e1f5fb221ca7c8206adedfa6192d2c20b70a800cccef953c6b8a598a7",
+ "width": 348,
+ "height": 656
+ },
+ {
+ "path": "slices/x/19.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/190.png",
+ "bytes": 10749,
+ "sha256": "d0e17daa68f9bd124c2d31e5f16221dccfcb9b2111c7e8e1b458cf2811957513",
+ "width": 345,
+ "height": 658
+ },
+ {
+ "path": "slices/x/191.png",
+ "bytes": 10089,
+ "sha256": "f4e1f34ddedc81149949d7ea95919474fdf2695e12b0ce500a00249361749afe",
+ "width": 358,
+ "height": 547
+ },
+ {
+ "path": "slices/x/192.png",
+ "bytes": 9892,
+ "sha256": "4edae607f20e59e9bc33715203f3f573a0df0519f2783816ce9579a8c713596f",
+ "width": 360,
+ "height": 561
+ },
+ {
+ "path": "slices/x/193.png",
+ "bytes": 9204,
+ "sha256": "9b57cc591193967974bc3de4d1e419a2b717cd3d256cfc6ae05b5aa8e09c160e",
+ "width": 362,
+ "height": 467
+ },
+ {
+ "path": "slices/x/194.png",
+ "bytes": 8577,
+ "sha256": "43934a71b3b58e7e90b8ef59d070ba7fee3427aace4a78f63acb683cf89c61b5",
+ "width": 346,
+ "height": 374
+ },
+ {
+ "path": "slices/x/195.png",
+ "bytes": 8537,
+ "sha256": "39c53ce86d4c12de4991e50b5ad0b14ae61c779955b1b415a9165cd1b1673f4f",
+ "width": 338,
+ "height": 443
+ },
+ {
+ "path": "slices/x/196.png",
+ "bytes": 8750,
+ "sha256": "26c084be4d121fb41e834252250116e15853122f5e3d65013ec39b0dbc1bd8ff",
+ "width": 337,
+ "height": 477
+ },
+ {
+ "path": "slices/x/197.png",
+ "bytes": 8688,
+ "sha256": "62c18a80e0908e112caa92461630cf76660101cfae91918ccc163fb3ffa00dfd",
+ "width": 347,
+ "height": 548
+ },
+ {
+ "path": "slices/x/198.png",
+ "bytes": 8226,
+ "sha256": "92658c36a6a65d5adf117ce3b642311006721bebccd84206345e9b8207668db4",
+ "width": 368,
+ "height": 550
+ },
+ {
+ "path": "slices/x/199.png",
+ "bytes": 7767,
+ "sha256": "81373dcd7c69cef949262c967c60a19837ee99a42b319df7672d298a1603ccb4",
+ "width": 358,
+ "height": 486
+ },
+ {
+ "path": "slices/x/20.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/200.png",
+ "bytes": 7696,
+ "sha256": "1a15346a250dcc323ae5a29a0f7f80a84deb46a4af74a3c2d17b7f27690a8a98",
+ "width": 358,
+ "height": 597
+ },
+ {
+ "path": "slices/x/201.png",
+ "bytes": 7396,
+ "sha256": "d9c1839657076ea965d667873bc850efbdb6702504c1d7b37c7fe31c8553c61b",
+ "width": 349,
+ "height": 598
+ },
+ {
+ "path": "slices/x/202.png",
+ "bytes": 7513,
+ "sha256": "23e816d5227af772a84ba7b1eddc08731cad30f1d92007ff2fa27c90e846135c",
+ "width": 349,
+ "height": 660
+ },
+ {
+ "path": "slices/x/203.png",
+ "bytes": 7217,
+ "sha256": "cb1f820426402bd8f9cdb836d33a1280d4149ccadcb00f35c5b1aa4eac19d0ee",
+ "width": 366,
+ "height": 626
+ },
+ {
+ "path": "slices/x/204.png",
+ "bytes": 7351,
+ "sha256": "a5bbcb53708e3210bc9588e0f9db5cb508801a44cf494eb6c6ce4ec3d6fc24b5",
+ "width": 367,
+ "height": 514
+ },
+ {
+ "path": "slices/x/205.png",
+ "bytes": 7430,
+ "sha256": "c4eda55360a07dd305b85fddcacf94608f7e5169a95eebc7ffb8fb65b0043a7c",
+ "width": 367,
+ "height": 599
+ },
+ {
+ "path": "slices/x/206.png",
+ "bytes": 7044,
+ "sha256": "4744d7fa8310c8e8f342352a7d0265718401bfa16af6864d6ae0e69fdef35fb0",
+ "width": 340,
+ "height": 538
+ },
+ {
+ "path": "slices/x/207.png",
+ "bytes": 6770,
+ "sha256": "58c9bb4a7775ddd057586f86e1de24318fc078e6b59f1d7b35dec5a8c616f1a7",
+ "width": 349,
+ "height": 592
+ },
+ {
+ "path": "slices/x/208.png",
+ "bytes": 6739,
+ "sha256": "f0bc3e3c77278dd0eb849e0ca449babeb03d8f334e0823a592d573f7a9c1f7c4",
+ "width": 349,
+ "height": 554
+ },
+ {
+ "path": "slices/x/209.png",
+ "bytes": 6474,
+ "sha256": "ca8867963bea8ab3b8e2c065cbf7080b089e6ade93da197e12dfbe420939630e",
+ "width": 353,
+ "height": 421
+ },
+ {
+ "path": "slices/x/21.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/210.png",
+ "bytes": 6211,
+ "sha256": "63c6edf06bcd0ae7480c52dca61d4774714620853c0e4d5d54dd8e3b6b4a49c0",
+ "width": 357,
+ "height": 418
+ },
+ {
+ "path": "slices/x/211.png",
+ "bytes": 5928,
+ "sha256": "c4fc0bd87b252045688c7ff196e66aaefe4a69d59366d62821828721a505f3bd",
+ "width": 358,
+ "height": 440
+ },
+ {
+ "path": "slices/x/212.png",
+ "bytes": 6222,
+ "sha256": "8dd181d4916e0b5044b7f1bcb5fd17cc23a4e211f4d29b44668c02a543462f9c",
+ "width": 344,
+ "height": 605
+ },
+ {
+ "path": "slices/x/213.png",
+ "bytes": 6019,
+ "sha256": "f93480d6dacffee5ce98d90903cc176e6bc60b9852618ff00fb4ca2192398d74",
+ "width": 354,
+ "height": 606
+ },
+ {
+ "path": "slices/x/214.png",
+ "bytes": 5832,
+ "sha256": "ad293bc80b4b53124e1c5ea9bfac83e2ff8a3cd5f6986314d0f4009bd2450705",
+ "width": 358,
+ "height": 490
+ },
+ {
+ "path": "slices/x/215.png",
+ "bytes": 5542,
+ "sha256": "658844db7d2f4951a1cf4ed081e553089730a6e661afcb1115c923066023da18",
+ "width": 363,
+ "height": 576
+ },
+ {
+ "path": "slices/x/216.png",
+ "bytes": 5290,
+ "sha256": "0da2db06752cd1b8102dee71b8389114ba4a719e5cb37f8017d592b493e3803c",
+ "width": 340,
+ "height": 578
+ },
+ {
+ "path": "slices/x/217.png",
+ "bytes": 5041,
+ "sha256": "4b24dd49a149ff6f4e7e4848b552a66aebcbe221dd4c7cc9486fd3f2dc4b04d9",
+ "width": 344,
+ "height": 489
+ },
+ {
+ "path": "slices/x/218.png",
+ "bytes": 4817,
+ "sha256": "274bfbc99eba6cb768cdcfb973dab312b532d2f0b0746efbe010854a4197a1dd",
+ "width": 361,
+ "height": 464
+ },
+ {
+ "path": "slices/x/219.png",
+ "bytes": 4668,
+ "sha256": "82d010088adc63a86be3d77848288d79fe1ebcd4ee07acd2775faad3c9fbabf6",
+ "width": 352,
+ "height": 527
+ },
+ {
+ "path": "slices/x/22.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/220.png",
+ "bytes": 4333,
+ "sha256": "ae404ba2f6ade39581216c8ee83b0c3cbdda34cb8b8f12c269b58fb28982e9a8",
+ "width": 364,
+ "height": 559
+ },
+ {
+ "path": "slices/x/221.png",
+ "bytes": 3683,
+ "sha256": "2d3c6cf8eddba37d68d35914f580ef8e56d2a848e3cde7f608b5c070bbae6a2e",
+ "width": 354,
+ "height": 560
+ },
+ {
+ "path": "slices/x/222.png",
+ "bytes": 2898,
+ "sha256": "3026a2e4b2b2ed3f631051902e320b35c16291f809674984c9cd59378b190a5e",
+ "width": 365,
+ "height": 504
+ },
+ {
+ "path": "slices/x/223.png",
+ "bytes": 2531,
+ "sha256": "7a1ed8f833b6f4b114543909de2c6da51fabc832e84f6735fdc5561058a1e8d6",
+ "width": 349,
+ "height": 575
+ },
+ {
+ "path": "slices/x/224.png",
+ "bytes": 1749,
+ "sha256": "f50b67881a94f2f5c846eadb4f29be5d19a8219f6874fcf985b139f1d6b6d19b",
+ "width": 361,
+ "height": 575
+ },
+ {
+ "path": "slices/x/225.png",
+ "bytes": 1015,
+ "sha256": "7d1505cecc1a8b5ae0e19e1b52c545d1a0dad9646c20e35e3eaff6d62e0c6c3c",
+ "width": 353,
+ "height": 392
+ },
+ {
+ "path": "slices/x/226.png",
+ "bytes": 1036,
+ "sha256": "9d401e45f68b03723142a8015b9df1f39957f3b585cd6f03c8cbdb862cd89f20",
+ "width": 343,
+ "height": 423
+ },
+ {
+ "path": "slices/x/227.png",
+ "bytes": 842,
+ "sha256": "e4f32b416cc6d6e93f4d2460265c570543b8e42942cf145dcae68b21cb811a4e",
+ "width": 324,
+ "height": 391
+ },
+ {
+ "path": "slices/x/228.png",
+ "bytes": 818,
+ "sha256": "c1b95324e3a6d2dc6fbadbe257f41b2be81fc63a2c1cc924555bcdba25a60bc5",
+ "width": 333,
+ "height": 377
+ },
+ {
+ "path": "slices/x/229.png",
+ "bytes": 771,
+ "sha256": "b8e38cca493a6a604faf11ad4bce9c3650a551c2f20bf05aa6cdca7c87accdc8",
+ "width": 318,
+ "height": 368
+ },
+ {
+ "path": "slices/x/23.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/230.png",
+ "bytes": 729,
+ "sha256": "e44f51dac2a5837d358c68e77e47469b8746806d61fe58ae1745537c88894a2c",
+ "width": 322,
+ "height": 379
+ },
+ {
+ "path": "slices/x/231.png",
+ "bytes": 674,
+ "sha256": "a0d1e45093aa7424f5a7f9f6ffe532590f75d796d35e72d4e13566c5d1932df0",
+ "width": 330,
+ "height": 337
+ },
+ {
+ "path": "slices/x/232.png",
+ "bytes": 636,
+ "sha256": "f82e70173be5328e4c620a43c8a7b6b68422f610e3dabb132339673700e42999",
+ "width": 293,
+ "height": 364
+ },
+ {
+ "path": "slices/x/233.png",
+ "bytes": 363,
+ "sha256": "4d3bccab339afcdccbe4275b0c6b6c092c3a309c90ae49a8365093c8d1cd5340",
+ "width": 182,
+ "height": 242
+ },
+ {
+ "path": "slices/x/234.png",
+ "bytes": 389,
+ "sha256": "6d93e3b68363bc3d40f1bfe9ae1fd700e89775f2726acbf4d76df519f0e38f7a",
+ "width": 223,
+ "height": 242
+ },
+ {
+ "path": "slices/x/235.png",
+ "bytes": 549,
+ "sha256": "cc50af48e7586b73e2e4c138c29694a54a4f105926cd92ee9ae20f41968579f1",
+ "width": 275,
+ "height": 348
+ },
+ {
+ "path": "slices/x/236.png",
+ "bytes": 342,
+ "sha256": "6f27d0f0ab8ce9f2beb55f2a101341f5c9cb339fa2b975b15fbc545b5ccb8b7f",
+ "width": 174,
+ "height": 290
+ },
+ {
+ "path": "slices/x/237.png",
+ "bytes": 209,
+ "sha256": "b19613c2d513528de3e4adf7a4058b0bb1d057e4cdffda4bb473b456d141ad92",
+ "width": 99,
+ "height": 166
+ },
+ {
+ "path": "slices/x/238.png",
+ "bytes": 122,
+ "sha256": "14b56d13a128ced9f664733cd9c8123b287025df49e61a2e09e37eba43bb8d35",
+ "width": 34,
+ "height": 43
+ },
+ {
+ "path": "slices/x/239.png",
+ "bytes": 173,
+ "sha256": "37f4f381c0e03641b8b83641fe99685d9ea87c3a76a38b0ee0fdb12fde7c654b",
+ "width": 149,
+ "height": 99
+ },
+ {
+ "path": "slices/x/24.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/240.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/241.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/242.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/243.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/244.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/245.png",
+ "bytes": 100,
+ "sha256": "233f1ca89a33ef5191accb21ab342237cbb92027d5140cda1c2a60cb8bb8552f",
+ "width": 5,
+ "height": 4
+ },
+ {
+ "path": "slices/x/246.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/247.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/248.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/249.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/25.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/250.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/251.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/252.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/253.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/254.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/255.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/256.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/257.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/258.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/259.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/26.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/260.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/261.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/262.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/263.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/27.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/28.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/29.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/30.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/31.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/32.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/33.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/34.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/35.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/36.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/37.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/38.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/x/39.png",
+ "bytes": 321,
+ "sha256": "d798830b8fbcd4ae43df36e692e9c729c75722e14c5842c547aa9f9dd64387f7",
+ "width": 139,
+ "height": 380
+ },
+ {
+ "path": "slices/x/40.png",
+ "bytes": 844,
+ "sha256": "329a2b12a05e970cd5afd8297709c9c18376cc87027e08c87cc53303518ccf5b",
+ "width": 310,
+ "height": 383
+ },
+ {
+ "path": "slices/x/41.png",
+ "bytes": 985,
+ "sha256": "4e710502e3f165b2a2dd87bd45d9537a39ee950fe8f2d575d87004d322825585",
+ "width": 310,
+ "height": 382
+ },
+ {
+ "path": "slices/x/42.png",
+ "bytes": 1019,
+ "sha256": "4371d803fd199a8bd851dc6aa168ccebe35f2e3240d4928ea13a1aca94612266",
+ "width": 305,
+ "height": 472
+ },
+ {
+ "path": "slices/x/43.png",
+ "bytes": 1185,
+ "sha256": "3e29ed2d8dac0e089434b6d475898e5af05f225ddc460b943b2cc7252f09bfea",
+ "width": 344,
+ "height": 499
+ },
+ {
+ "path": "slices/x/44.png",
+ "bytes": 1049,
+ "sha256": "700319c4c8a67e186a51098f54a1b4d37bc153c492d8a2aa1231b93c8d21842f",
+ "width": 345,
+ "height": 346
+ },
+ {
+ "path": "slices/x/45.png",
+ "bytes": 949,
+ "sha256": "ff42450ab26b6712c0cef742d27cf2d19027a261ce7181af1b7a23a32d88a7a3",
+ "width": 305,
+ "height": 349
+ },
+ {
+ "path": "slices/x/46.png",
+ "bytes": 1360,
+ "sha256": "0d7dee3ba0f787025b60083ff3da9dca19cc42c61305626f048459a6c85735a0",
+ "width": 341,
+ "height": 447
+ },
+ {
+ "path": "slices/x/47.png",
+ "bytes": 2199,
+ "sha256": "059c162fb38031c66fb5efa3da9407315633fcb130e80ed955244615787c3424",
+ "width": 347,
+ "height": 810
+ },
+ {
+ "path": "slices/x/48.png",
+ "bytes": 2388,
+ "sha256": "20aeeee6c2eb039fbfca371febe09abe6aafc50210e6b26969c7b734684d3207",
+ "width": 344,
+ "height": 759
+ },
+ {
+ "path": "slices/x/49.png",
+ "bytes": 2309,
+ "sha256": "3d88e5609b9112927f2a82f8472a3d40244f0674046d9678f6151d37d240b2b8",
+ "width": 336,
+ "height": 425
+ },
+ {
+ "path": "slices/x/50.png",
+ "bytes": 2395,
+ "sha256": "18ae63289a395c3b91badae8bcde01c45cbd08d576f2754f913d5f12059cd18f",
+ "width": 288,
+ "height": 479
+ },
+ {
+ "path": "slices/x/51.png",
+ "bytes": 2870,
+ "sha256": "479672b51b2dbc21d5a42f41801a25857684c87139edd463f23b00a95dc42ccd",
+ "width": 309,
+ "height": 509
+ },
+ {
+ "path": "slices/x/52.png",
+ "bytes": 3442,
+ "sha256": "d3f7af6dcb6fe3d551c34aac476d3880d366a7861f561545db13656b3c2b1f2a",
+ "width": 333,
+ "height": 570
+ },
+ {
+ "path": "slices/x/53.png",
+ "bytes": 3465,
+ "sha256": "eed5b727027f92255a73e46f8b0a591d0217a8701e61d4826f026d0086694d1c",
+ "width": 333,
+ "height": 570
+ },
+ {
+ "path": "slices/x/54.png",
+ "bytes": 3598,
+ "sha256": "a163297d30275c5d1045e9692819d0b453e873b47ce4415bd25d1c4b6e901030",
+ "width": 329,
+ "height": 537
+ },
+ {
+ "path": "slices/x/55.png",
+ "bytes": 4029,
+ "sha256": "ffc476d4f521223835feaa408ef131162d7995952a6881c4df4a921baa13fb8f",
+ "width": 336,
+ "height": 534
+ },
+ {
+ "path": "slices/x/56.png",
+ "bytes": 4108,
+ "sha256": "d2e03e3bd3541512f8a1c7dc218726ac6c19b645852bb177cb5d5ae34d955c75",
+ "width": 338,
+ "height": 534
+ },
+ {
+ "path": "slices/x/57.png",
+ "bytes": 4088,
+ "sha256": "deb534ed34fcec90c85b6851aa68c11d761660eb0aba8654ec22bdca326c82e2",
+ "width": 341,
+ "height": 446
+ },
+ {
+ "path": "slices/x/58.png",
+ "bytes": 4629,
+ "sha256": "89145393cc733b5bab611571045c5f1f5d5de2dfcd8be87fe880069634d52bdb",
+ "width": 341,
+ "height": 636
+ },
+ {
+ "path": "slices/x/59.png",
+ "bytes": 4732,
+ "sha256": "a0781cf8a32a9b21b761ab296a05ac09b849d9844b2a1a36583d12f5ff61743d",
+ "width": 323,
+ "height": 638
+ },
+ {
+ "path": "slices/x/60.png",
+ "bytes": 4722,
+ "sha256": "70c0b18950b7522b2d0c30946659f288acc373b4e425c4ce56f05a7d69475d06",
+ "width": 330,
+ "height": 636
+ },
+ {
+ "path": "slices/x/61.png",
+ "bytes": 4634,
+ "sha256": "fc7460aeb321dfaad83085070710faeac612cf48303437b8adafedce57b1e2e4",
+ "width": 331,
+ "height": 444
+ },
+ {
+ "path": "slices/x/62.png",
+ "bytes": 4990,
+ "sha256": "8e69dd527e5780299fd8817a1972592033a83a09b95ad713822617a541297b86",
+ "width": 334,
+ "height": 444
+ },
+ {
+ "path": "slices/x/63.png",
+ "bytes": 5300,
+ "sha256": "e0f761669cd0a5bd8cd9eac53801fd8fb06c73bf97bf2c06fa67ab3a548b88e0",
+ "width": 336,
+ "height": 445
+ },
+ {
+ "path": "slices/x/64.png",
+ "bytes": 5749,
+ "sha256": "e77889af962a9d22c058b9ccd4eacc4b212bde161af8725dcd2155a602f4214c",
+ "width": 333,
+ "height": 568
+ },
+ {
+ "path": "slices/x/65.png",
+ "bytes": 5667,
+ "sha256": "f702882a92cf70550d34329eb7da79409e888f7abd04f46c60f9057b74c26863",
+ "width": 335,
+ "height": 568
+ },
+ {
+ "path": "slices/x/66.png",
+ "bytes": 5967,
+ "sha256": "f4d062d4d88124161efa1f84f175e1e2c5b36fb6c14779158fcc00e36e6425e9",
+ "width": 334,
+ "height": 568
+ },
+ {
+ "path": "slices/x/67.png",
+ "bytes": 6145,
+ "sha256": "6c832c72a920f7bde909e17baa63f33faf7c40ae009a8340107fd1ab14ef3141",
+ "width": 328,
+ "height": 519
+ },
+ {
+ "path": "slices/x/68.png",
+ "bytes": 6504,
+ "sha256": "dc6d445a95705aae9f0f29a67b51811e73713b4479965277b8cf3c08ce358e9b",
+ "width": 338,
+ "height": 519
+ },
+ {
+ "path": "slices/x/69.png",
+ "bytes": 6782,
+ "sha256": "91c5298ee791fe9772ecffa07142b1146355faad2ec1cb67b88799c02505ebba",
+ "width": 345,
+ "height": 649
+ },
+ {
+ "path": "slices/x/70.png",
+ "bytes": 7093,
+ "sha256": "c222ab36e0528b8c88c81ebf914c2bfc99d388c05a5d36e1591cc92513facd8b",
+ "width": 350,
+ "height": 648
+ },
+ {
+ "path": "slices/x/71.png",
+ "bytes": 7268,
+ "sha256": "00f2bfeb31edb3203447b4878cc2cb1aa642a8701adb178920b69830663f8696",
+ "width": 356,
+ "height": 718
+ },
+ {
+ "path": "slices/x/72.png",
+ "bytes": 7289,
+ "sha256": "27989a86828eddd06e9016109be41cecf8d1d6ce7a2a6c9e4e4b40435a3502aa",
+ "width": 351,
+ "height": 541
+ },
+ {
+ "path": "slices/x/73.png",
+ "bytes": 7222,
+ "sha256": "8b029c50f45470524d3703073bf29f254dba7e0df246627b6ad1e97b4af3eb3f",
+ "width": 343,
+ "height": 474
+ },
+ {
+ "path": "slices/x/74.png",
+ "bytes": 7663,
+ "sha256": "130beca1bc63fa1b5991bddf9b19eae1edbe4f5f2b2911e63cccdaad605a1df0",
+ "width": 342,
+ "height": 485
+ },
+ {
+ "path": "slices/x/75.png",
+ "bytes": 7593,
+ "sha256": "b23fd9d05d90726f4bf8543d4b604663221e4b878dc309ba14bdac04120af37f",
+ "width": 343,
+ "height": 437
+ },
+ {
+ "path": "slices/x/76.png",
+ "bytes": 7859,
+ "sha256": "8a61ce4d9a7f12d9234e12fd8fc7841594ef68d8a8a56f326036b0b3e82591f0",
+ "width": 349,
+ "height": 503
+ },
+ {
+ "path": "slices/x/77.png",
+ "bytes": 8776,
+ "sha256": "8bb9a4a70261e98f158a8c8cc11a7869d6ea14127ad7c9431d6ba88a13d375f3",
+ "width": 356,
+ "height": 585
+ },
+ {
+ "path": "slices/x/78.png",
+ "bytes": 9127,
+ "sha256": "9e8bab7ac7390f0f3b58a8fa4508fa7d9a2b9ac3ba95bde9f537374179690596",
+ "width": 356,
+ "height": 584
+ },
+ {
+ "path": "slices/x/79.png",
+ "bytes": 8980,
+ "sha256": "0c15d961b5bd040d0ea7566bbca4eb6a510af6fe47ce390c488e8c9f7b94ee6c",
+ "width": 355,
+ "height": 541
+ },
+ {
+ "path": "slices/x/80.png",
+ "bytes": 8974,
+ "sha256": "35f8584ccc09d16454fb57c462779461ce4169389ad0e3333d5142ac0a0107e6",
+ "width": 346,
+ "height": 403
+ },
+ {
+ "path": "slices/x/81.png",
+ "bytes": 8960,
+ "sha256": "eb5b58152998f41243d5f8e0fdfada19956bb3429d005d13a868a6973b3294f2",
+ "width": 331,
+ "height": 362
+ },
+ {
+ "path": "slices/x/82.png",
+ "bytes": 9309,
+ "sha256": "767c43513b1782b28f8056079645898acf8288efaaf5150323858f22e6221520",
+ "width": 345,
+ "height": 343
+ },
+ {
+ "path": "slices/x/83.png",
+ "bytes": 9796,
+ "sha256": "14b65b82c8cc00f7e0b449b0248f665bda9b5740632a48ec63964b3ca3481aec",
+ "width": 353,
+ "height": 359
+ },
+ {
+ "path": "slices/x/84.png",
+ "bytes": 10284,
+ "sha256": "d498aac27c72971efcbca09a9aa299355c76e580d97cfbc8281fa8628c858c79",
+ "width": 340,
+ "height": 360
+ },
+ {
+ "path": "slices/x/85.png",
+ "bytes": 10408,
+ "sha256": "a1c54389f51bb9b295e79227483fd83fd7bf50f62db86555dc79187261acbb38",
+ "width": 354,
+ "height": 341
+ },
+ {
+ "path": "slices/x/86.png",
+ "bytes": 11139,
+ "sha256": "e1c5b1faeafdabbbd507cf43ab4ca087e0d59f11c1917d4b8fd58bc4254b01a5",
+ "width": 344,
+ "height": 480
+ },
+ {
+ "path": "slices/x/87.png",
+ "bytes": 11671,
+ "sha256": "d31e60b31d231630c5132a5ccef044e65299a3b45b91aeedb3e7881217223264",
+ "width": 349,
+ "height": 498
+ },
+ {
+ "path": "slices/x/88.png",
+ "bytes": 11566,
+ "sha256": "34a0501e00b3c9245275e7fc568a1c55327e41074c0dd899ed5d63db511379b8",
+ "width": 356,
+ "height": 400
+ },
+ {
+ "path": "slices/x/89.png",
+ "bytes": 11868,
+ "sha256": "1bd9f4e0ab8b83ba5620743668143000c0385a20661273dbc1e4e5a1aa1f4604",
+ "width": 356,
+ "height": 383
+ },
+ {
+ "path": "slices/x/90.png",
+ "bytes": 12152,
+ "sha256": "e0e3e17533998813efc731ffb4acfc44b76584e3e9ab006344cd926912aaf95b",
+ "width": 358,
+ "height": 384
+ },
+ {
+ "path": "slices/x/91.png",
+ "bytes": 12311,
+ "sha256": "0e1ee77369253fb4d091da49652b19fe79ab56633b801ef978882285994419ad",
+ "width": 356,
+ "height": 383
+ },
+ {
+ "path": "slices/x/92.png",
+ "bytes": 12915,
+ "sha256": "651f69ae52f2735f00db6c957e7dd08a7c0a1bcad154a83bc9897ae3bc963621",
+ "width": 355,
+ "height": 439
+ },
+ {
+ "path": "slices/x/93.png",
+ "bytes": 13573,
+ "sha256": "c021b467b60c91b6882b1b5ea5ac5514630ffc283c16c19fe0a92921553caed4",
+ "width": 348,
+ "height": 523
+ },
+ {
+ "path": "slices/x/94.png",
+ "bytes": 13675,
+ "sha256": "18d0265fa5ca6bbb783521a3ae0e274ddfe42a0a38ed90b5496dd469eaf0b396",
+ "width": 353,
+ "height": 464
+ },
+ {
+ "path": "slices/x/95.png",
+ "bytes": 13293,
+ "sha256": "196e7bdb26cadc55ba0bbcda8215ed017d064561b6bcf07be21ea7dadbb8563c",
+ "width": 353,
+ "height": 354
+ },
+ {
+ "path": "slices/x/96.png",
+ "bytes": 13564,
+ "sha256": "209321c1b84884f77cf9dca38d17ca1341a2d2eda2bfd83398b766f97cbead19",
+ "width": 354,
+ "height": 349
+ },
+ {
+ "path": "slices/x/97.png",
+ "bytes": 13587,
+ "sha256": "2a8045255d93e377b5ff2702286c77e2dd57b21012220b67c03596f6cbd61f59",
+ "width": 355,
+ "height": 346
+ },
+ {
+ "path": "slices/x/98.png",
+ "bytes": 13924,
+ "sha256": "6aa3f7b504fd158561438ea4fdf6afc761f4a1e9c794c67e2ed617c2b5061f43",
+ "width": 355,
+ "height": 356
+ },
+ {
+ "path": "slices/x/99.png",
+ "bytes": 14106,
+ "sha256": "76312f0d1cf791cd0c1ebc8ac6245354d8ec06ded91f611f454a392cfa2f939f",
+ "width": 357,
+ "height": 356
+ },
+ {
+ "path": "slices/y/00.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/01.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/02.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/03.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/04.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/05.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/06.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/07.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/08.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/09.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/10.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/100.png",
+ "bytes": 15937,
+ "sha256": "48a3525269f6f75d819ddbbb2d16a50c78240cb0ad97707fb99556a289e2826a",
+ "width": 348,
+ "height": 561
+ },
+ {
+ "path": "slices/y/101.png",
+ "bytes": 16056,
+ "sha256": "d6e59025a2a044392955267bfdc96cae8b635a2b8768de108522f3474c24b25e",
+ "width": 352,
+ "height": 559
+ },
+ {
+ "path": "slices/y/102.png",
+ "bytes": 15672,
+ "sha256": "2a831607777132208064486ffc692b2a806369accdd0db44ce19941e8faf54bd",
+ "width": 357,
+ "height": 406
+ },
+ {
+ "path": "slices/y/103.png",
+ "bytes": 15648,
+ "sha256": "886d3411a0ba86461475db699ce764a90a5f6ac1675176896438abff60b35182",
+ "width": 357,
+ "height": 396
+ },
+ {
+ "path": "slices/y/104.png",
+ "bytes": 15825,
+ "sha256": "5b4fa996b4feef132d4e1843b00965b87535131b03b49e6081023e8bc3c64181",
+ "width": 351,
+ "height": 417
+ },
+ {
+ "path": "slices/y/105.png",
+ "bytes": 15793,
+ "sha256": "db31d2262293fbd5c3da5a9c2b1dafca22ffd84ec8ef3b74269ddca8a2917f0a",
+ "width": 353,
+ "height": 370
+ },
+ {
+ "path": "slices/y/106.png",
+ "bytes": 16048,
+ "sha256": "67f8436d12eca6e19fdfd2e3538d18dcb7c5fcbf78dc9180a6d70c92f72d4d3a",
+ "width": 352,
+ "height": 385
+ },
+ {
+ "path": "slices/y/107.png",
+ "bytes": 16723,
+ "sha256": "30428940c9235338bbbcccc9654458523bafa353ed8e0e11009d769dea960f99",
+ "width": 355,
+ "height": 555
+ },
+ {
+ "path": "slices/y/108.png",
+ "bytes": 16703,
+ "sha256": "0fc4a3df2420e52a31300495c51243450c4308d9533e1924b405eed0b5db9328",
+ "width": 355,
+ "height": 509
+ },
+ {
+ "path": "slices/y/109.png",
+ "bytes": 17161,
+ "sha256": "3ea2b12c6de863045d92886fadd9d66b6bc2fc1aae033a7df7d7a4b69610a1ce",
+ "width": 353,
+ "height": 509
+ },
+ {
+ "path": "slices/y/11.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/110.png",
+ "bytes": 16671,
+ "sha256": "dba6a61edc631eafec1d39367fb5719dfc02bebc030354ab7846e08b7b758231",
+ "width": 352,
+ "height": 338
+ },
+ {
+ "path": "slices/y/111.png",
+ "bytes": 16854,
+ "sha256": "73bdfbb1f784697cf16810e8d06cd09d7f1a0d7fb1964561d8518983b1229160",
+ "width": 352,
+ "height": 377
+ },
+ {
+ "path": "slices/y/112.png",
+ "bytes": 16787,
+ "sha256": "25000308fa61fb1b79de0ef9479c02a4dfb51329e92957783d2b7ac21440386b",
+ "width": 352,
+ "height": 377
+ },
+ {
+ "path": "slices/y/113.png",
+ "bytes": 17136,
+ "sha256": "ddd07116242634d12fc5d94fed3ec9b779f2b86c8d8035c6494fdb614da3fd9f",
+ "width": 355,
+ "height": 404
+ },
+ {
+ "path": "slices/y/114.png",
+ "bytes": 17122,
+ "sha256": "150fb939b617bda1b62f6a4f0a3abb48fc0b0ce67a3d6cedf6fc0988b2caefff",
+ "width": 354,
+ "height": 377
+ },
+ {
+ "path": "slices/y/115.png",
+ "bytes": 17034,
+ "sha256": "29f994e4b18909b96f3df71c02deefddc6113cc114e5d47ec9de55758c58da51",
+ "width": 355,
+ "height": 349
+ },
+ {
+ "path": "slices/y/116.png",
+ "bytes": 17018,
+ "sha256": "b13d12e03af7be9f5bf1d15eb15cc1398badde169eadf56e08cc14e3f8b4b774",
+ "width": 355,
+ "height": 338
+ },
+ {
+ "path": "slices/y/117.png",
+ "bytes": 17967,
+ "sha256": "b27fffde4bfc08506c333578f638b8ef41df1dec84452fffa11a1a4a1026bba3",
+ "width": 356,
+ "height": 481
+ },
+ {
+ "path": "slices/y/118.png",
+ "bytes": 18074,
+ "sha256": "f047b9217eac40888a2bc7a894fa119318328e204265945653cad742d0e52750",
+ "width": 355,
+ "height": 482
+ },
+ {
+ "path": "slices/y/119.png",
+ "bytes": 18062,
+ "sha256": "fd3e0982ff7fecb2ea9334d787fd920b6eed6c6fa6710243d01ee3a48a7bf957",
+ "width": 356,
+ "height": 494
+ },
+ {
+ "path": "slices/y/12.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/120.png",
+ "bytes": 17789,
+ "sha256": "2e8f232abd07af9fc0d4a993c1eb8d900f315f54db866f5eb37e5b1ca9838e37",
+ "width": 356,
+ "height": 406
+ },
+ {
+ "path": "slices/y/121.png",
+ "bytes": 18537,
+ "sha256": "e08ff0d79f1384fc97d8ca09254e37e14aef5cabde9676721f1ab7cba69e2c6d",
+ "width": 356,
+ "height": 582
+ },
+ {
+ "path": "slices/y/122.png",
+ "bytes": 18551,
+ "sha256": "c0dc350f85cc5095d3da90838d9e532b0bb7a3ed1f93bb9868eed712331bd0fa",
+ "width": 356,
+ "height": 583
+ },
+ {
+ "path": "slices/y/123.png",
+ "bytes": 18189,
+ "sha256": "95a83a7e4446ef19c5c594bf598fbae56907ff482c7dece422a5292271b73a68",
+ "width": 357,
+ "height": 504
+ },
+ {
+ "path": "slices/y/124.png",
+ "bytes": 18246,
+ "sha256": "a8d79b3cf5c8cf2e5e0c10b711205346879f8990eb2f49e3c93cc3c002bb9899",
+ "width": 358,
+ "height": 502
+ },
+ {
+ "path": "slices/y/125.png",
+ "bytes": 17579,
+ "sha256": "b3bbc872dbffc30a7d3fb1e9da2c5373f54fd69ab12cb6249410b48dcab77722",
+ "width": 358,
+ "height": 380
+ },
+ {
+ "path": "slices/y/126.png",
+ "bytes": 17641,
+ "sha256": "36df33465bcb0cf7569e7267504657dc4cfb90391c897f6feedef49f80a9239b",
+ "width": 359,
+ "height": 372
+ },
+ {
+ "path": "slices/y/127.png",
+ "bytes": 17627,
+ "sha256": "7b56cdb3bb7a380f67214d12999f474491f848c47c5f2d63a67ab2f1d5d14d1f",
+ "width": 365,
+ "height": 372
+ },
+ {
+ "path": "slices/y/128.png",
+ "bytes": 18114,
+ "sha256": "3b52f3264a5f6b2b79980bd2109dbd7bbe41409130a12652c26224a477353548",
+ "width": 360,
+ "height": 440
+ },
+ {
+ "path": "slices/y/129.png",
+ "bytes": 18137,
+ "sha256": "9d4ebf1e1a1d0ff7369e6a4d2eca162dd79a745f43d59df646731c51a77f4570",
+ "width": 359,
+ "height": 441
+ },
+ {
+ "path": "slices/y/13.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/130.png",
+ "bytes": 17660,
+ "sha256": "272f009a5ea1ca542128d70b3379c6743249dbfc95a1380eb03a4025b96e4500",
+ "width": 360,
+ "height": 338
+ },
+ {
+ "path": "slices/y/131.png",
+ "bytes": 17788,
+ "sha256": "2e5c2a1e5641b99c2ae520862b36ffebcdcfc0ddaaa4b3e2880c470559ed2e5a",
+ "width": 361,
+ "height": 366
+ },
+ {
+ "path": "slices/y/132.png",
+ "bytes": 18522,
+ "sha256": "21c3ef96e9427e7fde8b8bda309cb99cd705775fbbbe02d2e5173ee3e5862caa",
+ "width": 363,
+ "height": 466
+ },
+ {
+ "path": "slices/y/133.png",
+ "bytes": 18317,
+ "sha256": "947f962ee4952be01a111ac52d92056ff7aae05ba8211325a35aa396fb3f4fd7",
+ "width": 362,
+ "height": 465
+ },
+ {
+ "path": "slices/y/134.png",
+ "bytes": 18327,
+ "sha256": "4159113a7535c0d6524865ff1bb0a0c153fc24a26a10ac13da3a89bd98657a2f",
+ "width": 362,
+ "height": 507
+ },
+ {
+ "path": "slices/y/135.png",
+ "bytes": 18442,
+ "sha256": "67c76b308a6d1e8ab1ebb9efa4b147a9492a086f251c124ce65548c0985f724d",
+ "width": 359,
+ "height": 469
+ },
+ {
+ "path": "slices/y/136.png",
+ "bytes": 18067,
+ "sha256": "ba0e2c6f1326563e061f8c3e4c47ac251ab6f1a590c7323fae445357fc3b272c",
+ "width": 368,
+ "height": 414
+ },
+ {
+ "path": "slices/y/137.png",
+ "bytes": 17871,
+ "sha256": "5d99835929321b4ae5b524b8ff0c70275019cd9877b1b9b5aa34f4f476eb826f",
+ "width": 367,
+ "height": 368
+ },
+ {
+ "path": "slices/y/138.png",
+ "bytes": 17668,
+ "sha256": "c1606945612ada73ab7cbe93b71c361a4880741355172bc3ec9697d1261276bd",
+ "width": 362,
+ "height": 341
+ },
+ {
+ "path": "slices/y/139.png",
+ "bytes": 17516,
+ "sha256": "14d9234444d2c92bff42f66536d29874bff6917af70cf3828e3d6cd880013c89",
+ "width": 363,
+ "height": 340
+ },
+ {
+ "path": "slices/y/14.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/140.png",
+ "bytes": 17638,
+ "sha256": "962b3cac1b514f26bc1b92f0fa67bdba38f59b795792c6554d6022aec1c18ade",
+ "width": 369,
+ "height": 363
+ },
+ {
+ "path": "slices/y/141.png",
+ "bytes": 18504,
+ "sha256": "c56483c86ed4469937d1f467a415d72611f1c797eecac65ca9918dc25f9ebc1c",
+ "width": 362,
+ "height": 513
+ },
+ {
+ "path": "slices/y/142.png",
+ "bytes": 19036,
+ "sha256": "c6564a69b67b8929349356e7a6667017aeca4a13d9fdbaea191536169dcb7357",
+ "width": 361,
+ "height": 637
+ },
+ {
+ "path": "slices/y/143.png",
+ "bytes": 18737,
+ "sha256": "e2ec476eb4e9cc08eb65a2ee6b30024aa0c0835a9567a2d7df7594a104152400",
+ "width": 363,
+ "height": 481
+ },
+ {
+ "path": "slices/y/144.png",
+ "bytes": 18059,
+ "sha256": "16bb3375a90d59945828bfc42ec095df9695f620d9cf86867c3e784b46342a48",
+ "width": 363,
+ "height": 365
+ },
+ {
+ "path": "slices/y/145.png",
+ "bytes": 17963,
+ "sha256": "f1c14e175c03662c61287f8b69a876e369214a500704d9b800bb68cb43915412",
+ "width": 368,
+ "height": 367
+ },
+ {
+ "path": "slices/y/146.png",
+ "bytes": 18016,
+ "sha256": "adf2262fb0acb317e34b14573583aff7ffacd579e8c790cdd4cca4f76b0018bc",
+ "width": 363,
+ "height": 362
+ },
+ {
+ "path": "slices/y/147.png",
+ "bytes": 18378,
+ "sha256": "f867f7bb8ae3d5bc7e1cb52e83582fa33a6adbd20854e5360671aab5dcc40dd1",
+ "width": 364,
+ "height": 440
+ },
+ {
+ "path": "slices/y/148.png",
+ "bytes": 18345,
+ "sha256": "3f6a8bcd565d0a8ef2f898d1ea4a6b576337b8bcc443a0e934d86923724c3d62",
+ "width": 364,
+ "height": 439
+ },
+ {
+ "path": "slices/y/149.png",
+ "bytes": 18090,
+ "sha256": "83f2c1343a2d9604314bd2ed7a68e734a95f6ccb94a44d5b3d2d75a91062b7ad",
+ "width": 365,
+ "height": 380
+ },
+ {
+ "path": "slices/y/15.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/150.png",
+ "bytes": 18200,
+ "sha256": "38b2f766f780e28c16cc1389d865e239fce98c00b47f866a9cd06b6751fafc37",
+ "width": 365,
+ "height": 445
+ },
+ {
+ "path": "slices/y/151.png",
+ "bytes": 18679,
+ "sha256": "3130b35c7fbfdfd6d2efe3a6a64257e1072e2e3c7604f1b0dd95c9a684037f43",
+ "width": 365,
+ "height": 643
+ },
+ {
+ "path": "slices/y/152.png",
+ "bytes": 18598,
+ "sha256": "5c66a8479db39b9dc51685b5001b0c2af41d9c76046fa1044afd26c171342aed",
+ "width": 366,
+ "height": 561
+ },
+ {
+ "path": "slices/y/153.png",
+ "bytes": 17805,
+ "sha256": "4a77d27f3597290e9dbae2dce6a137076dba620a11f3b7d1c642c7c14bcf3867",
+ "width": 368,
+ "height": 349
+ },
+ {
+ "path": "slices/y/154.png",
+ "bytes": 18144,
+ "sha256": "db8ff80524f9929d68b74cc3427a71d6343cc883102e6a58288b9d0e10c59f09",
+ "width": 372,
+ "height": 459
+ },
+ {
+ "path": "slices/y/155.png",
+ "bytes": 18129,
+ "sha256": "2ad57c98b1703353fc193839bbb7a48edfc611b6dd1ffb5a5899ed3491254da3",
+ "width": 368,
+ "height": 460
+ },
+ {
+ "path": "slices/y/156.png",
+ "bytes": 18400,
+ "sha256": "793ec379072b3511e8dcff6d84df86df195439da2eb9c3ebc339c6e76c2a0b5f",
+ "width": 367,
+ "height": 505
+ },
+ {
+ "path": "slices/y/157.png",
+ "bytes": 17550,
+ "sha256": "4d11b3d0cc3991bb3150b53024df542024f9261208d1c8d285efb61d01510e67",
+ "width": 368,
+ "height": 397
+ },
+ {
+ "path": "slices/y/158.png",
+ "bytes": 17334,
+ "sha256": "057f5754aba3bdd1875b4554799465fdaa56a11e55b2fe3e0971587f1c919205",
+ "width": 367,
+ "height": 385
+ },
+ {
+ "path": "slices/y/159.png",
+ "bytes": 17446,
+ "sha256": "a39e014dea0e9810680f46001a1f7d5c33363da19b318aaa9ce4b3e2b8a79cc2",
+ "width": 369,
+ "height": 404
+ },
+ {
+ "path": "slices/y/16.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/160.png",
+ "bytes": 17205,
+ "sha256": "1d148ca775fdb07520d28fa03cbc507a41199c32a9764abf71ec070fabbce673",
+ "width": 369,
+ "height": 424
+ },
+ {
+ "path": "slices/y/161.png",
+ "bytes": 17157,
+ "sha256": "2c8a4245c7da35d9f2004745e896963f7ba767c2f1ee79f00c4eb6a298ed5fd9",
+ "width": 370,
+ "height": 424
+ },
+ {
+ "path": "slices/y/162.png",
+ "bytes": 16633,
+ "sha256": "c70a08dcdfc94a6e5f893690eabd6d40d017a5a1e1fe43d645e5d56c39bb896c",
+ "width": 369,
+ "height": 362
+ },
+ {
+ "path": "slices/y/163.png",
+ "bytes": 16937,
+ "sha256": "433d85eb9c9c679c366976c43a81ff2f281457d72bc07915839ba6e66d932dce",
+ "width": 369,
+ "height": 436
+ },
+ {
+ "path": "slices/y/164.png",
+ "bytes": 16627,
+ "sha256": "da20ec40773d6c45697dc1158475eda53a1e444f502b0f9210d41a9f8dae342e",
+ "width": 368,
+ "height": 428
+ },
+ {
+ "path": "slices/y/165.png",
+ "bytes": 16845,
+ "sha256": "9a77da5555bc5503146f138689ccaf6e0cff23ff30fdd75f181226fef466dd90",
+ "width": 371,
+ "height": 529
+ },
+ {
+ "path": "slices/y/166.png",
+ "bytes": 16619,
+ "sha256": "fb1d669d3b8f35c62f7c394ae5fd4b64dd80b576a83ace3d08437aab2fe554c1",
+ "width": 370,
+ "height": 531
+ },
+ {
+ "path": "slices/y/167.png",
+ "bytes": 15620,
+ "sha256": "c7d4b280870dace7ddbcbb48f9c1dd4122ce4c688de065659703ea237c3beb37",
+ "width": 365,
+ "height": 463
+ },
+ {
+ "path": "slices/y/168.png",
+ "bytes": 15585,
+ "sha256": "1f7dd44e9e4bb84543ff77faac956a0a607e9c9f9e8a9e9fbed96ae5c947f947",
+ "width": 362,
+ "height": 426
+ },
+ {
+ "path": "slices/y/169.png",
+ "bytes": 16004,
+ "sha256": "2e05727d9ca9b8ac21405fd1c3a48be43c8d75f1b1673e7e94bf70607d1a4961",
+ "width": 372,
+ "height": 466
+ },
+ {
+ "path": "slices/y/17.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/170.png",
+ "bytes": 16671,
+ "sha256": "757489d6ed8f6449023388b2228e6c91650082f2bd48569ccd74754dbf018c9d",
+ "width": 372,
+ "height": 648
+ },
+ {
+ "path": "slices/y/171.png",
+ "bytes": 15995,
+ "sha256": "89641600b7bb3dbfafbe6074559c7906b55662df77bb337fdc19d0f547bb48d4",
+ "width": 359,
+ "height": 648
+ },
+ {
+ "path": "slices/y/172.png",
+ "bytes": 15450,
+ "sha256": "cf2236612d9ebf908174eaf0787b7683fa9398714aed69860f9e01d0aa2c8d99",
+ "width": 374,
+ "height": 466
+ },
+ {
+ "path": "slices/y/173.png",
+ "bytes": 14843,
+ "sha256": "bdaaf93ee092ad0db05eb0cba11609f6c82685d867100f73d00d64f2da52e329",
+ "width": 360,
+ "height": 399
+ },
+ {
+ "path": "slices/y/174.png",
+ "bytes": 15156,
+ "sha256": "8e67ae63f801dc393a52712249f016568dca54332bb401185e30e1e1ea74c70c",
+ "width": 361,
+ "height": 553
+ },
+ {
+ "path": "slices/y/175.png",
+ "bytes": 14623,
+ "sha256": "77c5a6e199bbdc34eb307ee816f97e2cc9831c54091a5c068a378e957b86325b",
+ "width": 360,
+ "height": 535
+ },
+ {
+ "path": "slices/y/176.png",
+ "bytes": 13966,
+ "sha256": "b795b4d3c96a5f937cf0a297f96cb6774da04bd24d09ebd44963ce496c8ff877",
+ "width": 371,
+ "height": 409
+ },
+ {
+ "path": "slices/y/177.png",
+ "bytes": 13480,
+ "sha256": "108f29a8e408b7988bf3439dcf1f27abe652582511a3721d7627f88e8b314573",
+ "width": 361,
+ "height": 430
+ },
+ {
+ "path": "slices/y/178.png",
+ "bytes": 13185,
+ "sha256": "d4dd154dec2168ea8716520a433182db79aa7fa86c0b5337b5c0c5a9be67d003",
+ "width": 361,
+ "height": 469
+ },
+ {
+ "path": "slices/y/179.png",
+ "bytes": 12874,
+ "sha256": "24441b8d0b48449335068ac17b975318fffdbf86a0db89816d9d850f09ca813e",
+ "width": 363,
+ "height": 456
+ },
+ {
+ "path": "slices/y/18.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/180.png",
+ "bytes": 11841,
+ "sha256": "627c935fd47941639049e2f81221ccb95e6ea6c8baf82ab723614f792eae73e1",
+ "width": 358,
+ "height": 397
+ },
+ {
+ "path": "slices/y/181.png",
+ "bytes": 11240,
+ "sha256": "801548897aa1ab924330b5a5e7c8878a23e187beb967cccedf59fbee2cc52eba",
+ "width": 357,
+ "height": 355
+ },
+ {
+ "path": "slices/y/182.png",
+ "bytes": 11409,
+ "sha256": "a1633664e022312b4105b078d5aeff5ef8d43b5db395c477349e8502c76dbd86",
+ "width": 362,
+ "height": 426
+ },
+ {
+ "path": "slices/y/183.png",
+ "bytes": 11274,
+ "sha256": "af6a33d3a6550c6ee277a119d3f507576668789490b914985fcee9b28beed3be",
+ "width": 358,
+ "height": 425
+ },
+ {
+ "path": "slices/y/184.png",
+ "bytes": 10779,
+ "sha256": "35fb8cb64a78aa2062acf7f22c2e89fef7f6e852fef96f529cf1462074c7fefa",
+ "width": 357,
+ "height": 382
+ },
+ {
+ "path": "slices/y/185.png",
+ "bytes": 10606,
+ "sha256": "92483b14fcf1655aa359ac68ef4693be2102ad0269a99a599ae9cc8da50794af",
+ "width": 357,
+ "height": 424
+ },
+ {
+ "path": "slices/y/186.png",
+ "bytes": 10104,
+ "sha256": "692eb220d887bf7d5755eb47476d43e91a96dd282a15226da02eab14fbfb7dd6",
+ "width": 364,
+ "height": 426
+ },
+ {
+ "path": "slices/y/187.png",
+ "bytes": 9368,
+ "sha256": "1bdb0f5c30750540c15c71e531ed575a996137baba22f0b87e2ab6bcaa9da74f",
+ "width": 355,
+ "height": 337
+ },
+ {
+ "path": "slices/y/188.png",
+ "bytes": 8945,
+ "sha256": "8086ce5eb7d234785aed10ce48d1698d0abb5457e4afe6002a9845dcbceddd9f",
+ "width": 357,
+ "height": 337
+ },
+ {
+ "path": "slices/y/189.png",
+ "bytes": 8904,
+ "sha256": "d58e5ad5c31c28cee0665598ee947a76c7a8134dc1ec10c11d7687f543e49f58",
+ "width": 363,
+ "height": 423
+ },
+ {
+ "path": "slices/y/19.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/190.png",
+ "bytes": 9036,
+ "sha256": "65d930fcf095db416e0926535004d6f6f47aa4856b09fd479dd1dc0c39fda1f4",
+ "width": 356,
+ "height": 677
+ },
+ {
+ "path": "slices/y/191.png",
+ "bytes": 8673,
+ "sha256": "ab0c93480fcabab371acb7e435c322744acb1238016f987e8dff6e22c646f586",
+ "width": 354,
+ "height": 766
+ },
+ {
+ "path": "slices/y/192.png",
+ "bytes": 8161,
+ "sha256": "eed58cca39f55a2aac8abf04593ab361ac575b78d38f8394e084ea62da342341",
+ "width": 363,
+ "height": 584
+ },
+ {
+ "path": "slices/y/193.png",
+ "bytes": 7416,
+ "sha256": "6a6358830c465b7aed0a5b32717664ba505429b8bb565b75ce70ae85a7a3ad96",
+ "width": 356,
+ "height": 432
+ },
+ {
+ "path": "slices/y/194.png",
+ "bytes": 7028,
+ "sha256": "997ec6e6f143ffdf95b1a7ead6f5474d074df84eaa8e39c4ff4efe18daf62863",
+ "width": 353,
+ "height": 420
+ },
+ {
+ "path": "slices/y/195.png",
+ "bytes": 6733,
+ "sha256": "cf8a8f65b2e279fbb0f675f02c890f5bb2a4ae471e85a2b1bea86a229a8d4dec",
+ "width": 356,
+ "height": 405
+ },
+ {
+ "path": "slices/y/196.png",
+ "bytes": 6539,
+ "sha256": "5d5c0c474d840de7446e0a4d2dcfcf63f60d7dc66ec8886b0f8f99ad09d3a510",
+ "width": 356,
+ "height": 525
+ },
+ {
+ "path": "slices/y/197.png",
+ "bytes": 6684,
+ "sha256": "067625be11dc12d7bbfcb3b213677ddfc2ca30d0364744e37695b9e1289262e6",
+ "width": 359,
+ "height": 525
+ },
+ {
+ "path": "slices/y/198.png",
+ "bytes": 6625,
+ "sha256": "25ddf74e9c0b4e2e61745244fedf10cf25cf3fbc5c7e4d6d9e2d753dbbab8fbe",
+ "width": 356,
+ "height": 448
+ },
+ {
+ "path": "slices/y/199.png",
+ "bytes": 6357,
+ "sha256": "898ee2897d9c7da7bbeff1837eb0188b8f37311e52ce96272e3c5224da8cd05d",
+ "width": 360,
+ "height": 450
+ },
+ {
+ "path": "slices/y/20.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/200.png",
+ "bytes": 6286,
+ "sha256": "1f9e1a353ea387e13c40a7fa5a013157d253350d0f79b53481f5e065c5648058",
+ "width": 360,
+ "height": 387
+ },
+ {
+ "path": "slices/y/201.png",
+ "bytes": 6683,
+ "sha256": "24c97d13fbef411ab176f7e71384da53a8b229465a4392da2e84af5e83901ae9",
+ "width": 356,
+ "height": 388
+ },
+ {
+ "path": "slices/y/202.png",
+ "bytes": 6492,
+ "sha256": "f5addf9b0a8254a9352865348d28d6655305ef48bfa967723b3b645ba729f54a",
+ "width": 357,
+ "height": 421
+ },
+ {
+ "path": "slices/y/203.png",
+ "bytes": 6310,
+ "sha256": "e5332c36284a443adc41ecb90753b386b47709875cbd9cd2ec5dafd76cfe7550",
+ "width": 353,
+ "height": 449
+ },
+ {
+ "path": "slices/y/204.png",
+ "bytes": 6180,
+ "sha256": "bb54a794d886824aa9a7fc85e17800e9ce9b726483cd49bda71a3f4919283e85",
+ "width": 357,
+ "height": 533
+ },
+ {
+ "path": "slices/y/205.png",
+ "bytes": 5601,
+ "sha256": "14f9e1b1275b5300d1601709f37347effd8bcdf284d4ba213923751d469e33d9",
+ "width": 325,
+ "height": 434
+ },
+ {
+ "path": "slices/y/206.png",
+ "bytes": 5576,
+ "sha256": "dd741bcf9327a6c7ff4817d174767d4cafd0289b5df67406dc588f34544fc326",
+ "width": 354,
+ "height": 514
+ },
+ {
+ "path": "slices/y/207.png",
+ "bytes": 5382,
+ "sha256": "0e78cf6825210be842061c90f2201807509b6edcda5fd38ca32d5b18712ecba1",
+ "width": 358,
+ "height": 520
+ },
+ {
+ "path": "slices/y/208.png",
+ "bytes": 5166,
+ "sha256": "89205b8b10069018db4f73fa72ac889b3ec571a775b3ff932821adf75db07583",
+ "width": 358,
+ "height": 573
+ },
+ {
+ "path": "slices/y/209.png",
+ "bytes": 4979,
+ "sha256": "736c8a24ed74235268760dd456aca72b44490d5ae2261279b88b954f013af979",
+ "width": 356,
+ "height": 592
+ },
+ {
+ "path": "slices/y/21.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/210.png",
+ "bytes": 4546,
+ "sha256": "132d169e13a75df224ae0a96854c80eafd42af11499fa2f151b80301b83e8a74",
+ "width": 355,
+ "height": 460
+ },
+ {
+ "path": "slices/y/211.png",
+ "bytes": 4771,
+ "sha256": "c075ba2b6165c4c2b000066e613f50b2be51f8d3a7609f429c6a9d1dea470903",
+ "width": 351,
+ "height": 457
+ },
+ {
+ "path": "slices/y/212.png",
+ "bytes": 4554,
+ "sha256": "602ac18f150dc3b84f06a6c7c6ccbe5ff9f5c0b8f6ea8b6a67a66bba50ba3886",
+ "width": 350,
+ "height": 438
+ },
+ {
+ "path": "slices/y/213.png",
+ "bytes": 4406,
+ "sha256": "8a254e6e66e9ffa311579abcc5bce6ce070d4afecbe72a9c830a1ca1881acd74",
+ "width": 343,
+ "height": 480
+ },
+ {
+ "path": "slices/y/214.png",
+ "bytes": 4446,
+ "sha256": "9d1fe18971c7ade5b7797657994ac5902022daaac9ec206ea1bc1c7e8bbe91a1",
+ "width": 345,
+ "height": 480
+ },
+ {
+ "path": "slices/y/215.png",
+ "bytes": 4689,
+ "sha256": "52d4a841d61bf41114c35a65ed33d8b753b5c0c9cabdb8aea42f18970e0f2662",
+ "width": 347,
+ "height": 625
+ },
+ {
+ "path": "slices/y/216.png",
+ "bytes": 4874,
+ "sha256": "a8b337a3fba2a3c9d1fcef465bf666ed4fef7f3fe835681794740806fb83af58",
+ "width": 349,
+ "height": 672
+ },
+ {
+ "path": "slices/y/217.png",
+ "bytes": 4517,
+ "sha256": "3456432c98da208cccee3d2db5152a1c0781405c28cc4a93a4edbca0af1ac090",
+ "width": 349,
+ "height": 574
+ },
+ {
+ "path": "slices/y/218.png",
+ "bytes": 4157,
+ "sha256": "48e653e6ba2b35c60708a49afab462d0175aa02aac1cbfa667deb8b54abe5a8c",
+ "width": 348,
+ "height": 535
+ },
+ {
+ "path": "slices/y/219.png",
+ "bytes": 3980,
+ "sha256": "fe9da402c26ef5a91ec4b9aa66366417eb21fff610b6d067779f0426a82e5f98",
+ "width": 343,
+ "height": 477
+ },
+ {
+ "path": "slices/y/22.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/220.png",
+ "bytes": 3634,
+ "sha256": "3ece6dd033ffb43e98c97af4c185c9d38493047f25276e2531a531fead3b28f7",
+ "width": 341,
+ "height": 437
+ },
+ {
+ "path": "slices/y/221.png",
+ "bytes": 4183,
+ "sha256": "9f089843c90da550f24a2a343480cabeb40cd01fc61986533d300a9d899af710",
+ "width": 346,
+ "height": 981
+ },
+ {
+ "path": "slices/y/222.png",
+ "bytes": 4053,
+ "sha256": "7405a40dfe219bea742a11212e06e2df670d4ddeb4f946128c9d27435733e1a1",
+ "width": 347,
+ "height": 981
+ },
+ {
+ "path": "slices/y/223.png",
+ "bytes": 3327,
+ "sha256": "c74b0015a3fe3a23afc396f01cd0b415fd58aef96ac5ddfecfcefc2815947cc0",
+ "width": 350,
+ "height": 716
+ },
+ {
+ "path": "slices/y/224.png",
+ "bytes": 2610,
+ "sha256": "873ea8d2e8275cd587e3b4c1287ddfc47b506e76ed2c545757d16ce567095dbe",
+ "width": 350,
+ "height": 715
+ },
+ {
+ "path": "slices/y/225.png",
+ "bytes": 1405,
+ "sha256": "e62c68940ea1a4eb7044fb9781eb311335d12ccfa3b4ed27845d6afc8551822a",
+ "width": 338,
+ "height": 538
+ },
+ {
+ "path": "slices/y/226.png",
+ "bytes": 814,
+ "sha256": "5815e741972c53d90e33ac943294ecbaacc9a0bdb2d98d74e75613974daf8684",
+ "width": 349,
+ "height": 222
+ },
+ {
+ "path": "slices/y/227.png",
+ "bytes": 823,
+ "sha256": "b01d0c1dd528066016a1548cf191100f0b3f719d71dc2c3f72730ef4a189d651",
+ "width": 348,
+ "height": 268
+ },
+ {
+ "path": "slices/y/228.png",
+ "bytes": 719,
+ "sha256": "1bdde2eeb0188f4a7a880d788477151b04e45b06cad87a2185ebc6df3c0f0a20",
+ "width": 336,
+ "height": 228
+ },
+ {
+ "path": "slices/y/229.png",
+ "bytes": 682,
+ "sha256": "014b3bab44dc73b4f98201809ec6673d1f168ca2f1677417976c44378149c56f",
+ "width": 321,
+ "height": 233
+ },
+ {
+ "path": "slices/y/23.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/230.png",
+ "bytes": 556,
+ "sha256": "c4238b5481abdc7957174c53fe37791d9da8fc4a0e3eea1cb9882d070748e846",
+ "width": 332,
+ "height": 191
+ },
+ {
+ "path": "slices/y/231.png",
+ "bytes": 429,
+ "sha256": "06f46fe110ee6ef530ffc520c75e86479c48ff04a7aeede99e2d397187ace92f",
+ "width": 243,
+ "height": 157
+ },
+ {
+ "path": "slices/y/232.png",
+ "bytes": 481,
+ "sha256": "aff0d722feefc65295cc05389542c5fac85d452dfff11771a293e56adc74e067",
+ "width": 301,
+ "height": 200
+ },
+ {
+ "path": "slices/y/233.png",
+ "bytes": 353,
+ "sha256": "80499ce0fc62fe1134b0bcf99903ad847ff2a8bfdff2832e4b95e5ebb6e06601",
+ "width": 239,
+ "height": 152
+ },
+ {
+ "path": "slices/y/234.png",
+ "bytes": 398,
+ "sha256": "5d690c9e00756752b820eb78a937623bf890f1b51f64210f9850efdb7a6f236c",
+ "width": 310,
+ "height": 171
+ },
+ {
+ "path": "slices/y/235.png",
+ "bytes": 281,
+ "sha256": "a3d1090b49983a1a17e7559f0a468dbf23f37015e2d2b2bf6fec22241e2ce2ff",
+ "width": 200,
+ "height": 140
+ },
+ {
+ "path": "slices/y/236.png",
+ "bytes": 286,
+ "sha256": "e90e243a93365ef9ba560f851db58b8c21d86d869f2bc2f17356843a72082c24",
+ "width": 227,
+ "height": 119
+ },
+ {
+ "path": "slices/y/237.png",
+ "bytes": 283,
+ "sha256": "a4e1c1f91ed35953e9bf6f64c0bdd0bf460fd5ef01082914d092bb52120c326f",
+ "width": 290,
+ "height": 118
+ },
+ {
+ "path": "slices/y/238.png",
+ "bytes": 184,
+ "sha256": "58db3cb67aaab4b0998797fb8f006943865e846357a69453181b0b927b50e268",
+ "width": 192,
+ "height": 74
+ },
+ {
+ "path": "slices/y/239.png",
+ "bytes": 172,
+ "sha256": "3f38f1618c9f09d34b894cc9808827e9f2a06dde5072a763557a4c1d3cb216b0",
+ "width": 190,
+ "height": 65
+ },
+ {
+ "path": "slices/y/24.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/240.png",
+ "bytes": 158,
+ "sha256": "f46cc6c467b7b005cfed253180dff3fc17128825d4200ec9c0631ae339c4b707",
+ "width": 99,
+ "height": 81
+ },
+ {
+ "path": "slices/y/241.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/242.png",
+ "bytes": 178,
+ "sha256": "a2b1f2884d2cb8e39927ef0f5efca8ae0de85fce5dbdac8abe06758f1e51e634",
+ "width": 148,
+ "height": 92
+ },
+ {
+ "path": "slices/y/243.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/244.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/245.png",
+ "bytes": 162,
+ "sha256": "035afaa4bacb3ba6e4301e6d9d6f43c29d24a1707f295c1d79cfc428b2a8a8d9",
+ "width": 104,
+ "height": 116
+ },
+ {
+ "path": "slices/y/246.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/247.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/248.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/249.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/25.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/250.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/251.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/252.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/253.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/254.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/255.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/256.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/257.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/258.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/259.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/26.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/260.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/261.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/262.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/263.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/27.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/28.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/29.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/30.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/31.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/32.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/33.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/34.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/35.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/36.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/37.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/38.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/39.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/y/40.png",
+ "bytes": 309,
+ "sha256": "99c26b6b73f4ed22d82fc7e56dae027a3b4252cd3c290f6b0a48bbb37b233a82",
+ "width": 160,
+ "height": 203
+ },
+ {
+ "path": "slices/y/41.png",
+ "bytes": 749,
+ "sha256": "a409937f7e8bc9833ada1840dde0c5162605d3a0847609a60479f9a351759a43",
+ "width": 260,
+ "height": 280
+ },
+ {
+ "path": "slices/y/42.png",
+ "bytes": 1027,
+ "sha256": "709ad4588d6b41f6fd586cad99678105bebea216ab85c3171e25a7aaa7a50ba9",
+ "width": 327,
+ "height": 311
+ },
+ {
+ "path": "slices/y/43.png",
+ "bytes": 1140,
+ "sha256": "f7095717016dd2eb215174b765a9a2350670a1e56ad8937aae4be96eba1d7eb6",
+ "width": 346,
+ "height": 416
+ },
+ {
+ "path": "slices/y/44.png",
+ "bytes": 1269,
+ "sha256": "56a898a0d7946303fa7b03ce6bc5d631e8d463d0d30ddcabe02398b537cb4f37",
+ "width": 336,
+ "height": 465
+ },
+ {
+ "path": "slices/y/45.png",
+ "bytes": 1299,
+ "sha256": "c68ed129b1feab7d7c17938c8bb920a40cb39d7db4ec0ce6cd15c9984ec6bfe4",
+ "width": 334,
+ "height": 465
+ },
+ {
+ "path": "slices/y/46.png",
+ "bytes": 1356,
+ "sha256": "e6c9fe6b7db0bc01cd84e95b569f14d6276d24e102716cd229f857256ae07110",
+ "width": 347,
+ "height": 340
+ },
+ {
+ "path": "slices/y/47.png",
+ "bytes": 2098,
+ "sha256": "7dee9afbdec9e22e2643099f020a3243ec4c6c30ad80ef1d1786ea49973e1495",
+ "width": 348,
+ "height": 703
+ },
+ {
+ "path": "slices/y/48.png",
+ "bytes": 2385,
+ "sha256": "6e5eeac752bc9ace69950dee68649124d0f5422665efdf10f65a53e5594961cc",
+ "width": 346,
+ "height": 740
+ },
+ {
+ "path": "slices/y/49.png",
+ "bytes": 1955,
+ "sha256": "c6165e50d165aff1b41b835e2bd187525e521a3ae705971d9a89158ad644513d",
+ "width": 341,
+ "height": 438
+ },
+ {
+ "path": "slices/y/50.png",
+ "bytes": 2179,
+ "sha256": "ff2f7176399d543db33a1a6432f47143c2f32cd75d7a3752019a7a7969bd8cee",
+ "width": 330,
+ "height": 418
+ },
+ {
+ "path": "slices/y/51.png",
+ "bytes": 3222,
+ "sha256": "8961f761c5e963c439cf3969978018cf49181225965c41efbe00ed56302b9351",
+ "width": 350,
+ "height": 523
+ },
+ {
+ "path": "slices/y/52.png",
+ "bytes": 4248,
+ "sha256": "9759b9a579a342bc0873d5330fd24871119f3fc8d24af151b1b6226e55699d8d",
+ "width": 350,
+ "height": 703
+ },
+ {
+ "path": "slices/y/53.png",
+ "bytes": 4516,
+ "sha256": "02720e4aac68232a66b93e8eab385929d004c5c39d24ea332bcdcdee8d9987b6",
+ "width": 347,
+ "height": 703
+ },
+ {
+ "path": "slices/y/54.png",
+ "bytes": 4808,
+ "sha256": "89dc6aecdfe49a9b33dfd2cd010853e79257cf1f80cb1dd47d8e40a1e1a2c37c",
+ "width": 308,
+ "height": 604
+ },
+ {
+ "path": "slices/y/55.png",
+ "bytes": 5605,
+ "sha256": "469f381522379af727998088b1dd2ebecfad37e12688721e641821a6d4423372",
+ "width": 322,
+ "height": 733
+ },
+ {
+ "path": "slices/y/56.png",
+ "bytes": 5517,
+ "sha256": "66d8eb55b51e1bce534655a1fec2d5a36e4ea48a90c2d5a36a09eb72541e3368",
+ "width": 323,
+ "height": 732
+ },
+ {
+ "path": "slices/y/57.png",
+ "bytes": 5516,
+ "sha256": "4da398a7f05416a52924624d322c5e646475c6fbc4c994410cde3c00fb1c1fe5",
+ "width": 338,
+ "height": 708
+ },
+ {
+ "path": "slices/y/58.png",
+ "bytes": 5160,
+ "sha256": "215a0a6e607e9fed1f7b1f6d5302ceef40ca6815ae59cf3e1eb460b2cb3279e3",
+ "width": 337,
+ "height": 569
+ },
+ {
+ "path": "slices/y/59.png",
+ "bytes": 4960,
+ "sha256": "6c8848f84aec74e9faeab8f787375e2ff31d4b25c7103de72a004207b8b2453b",
+ "width": 306,
+ "height": 478
+ },
+ {
+ "path": "slices/y/60.png",
+ "bytes": 5137,
+ "sha256": "e209e7462f5307f4ff4c9e53a4843b2da018bf3a94ad4d971c12fcdce23c002f",
+ "width": 328,
+ "height": 480
+ },
+ {
+ "path": "slices/y/61.png",
+ "bytes": 5193,
+ "sha256": "b35cbf82787f863776fbec87d91a05efba5f56f78d57585911ffd4b1a00d7edc",
+ "width": 347,
+ "height": 425
+ },
+ {
+ "path": "slices/y/62.png",
+ "bytes": 5730,
+ "sha256": "912fe883e47bef63fcffeffa726f90fc6617f0cb802bc2cf9de0004c8c6a1aec",
+ "width": 348,
+ "height": 702
+ },
+ {
+ "path": "slices/y/63.png",
+ "bytes": 6108,
+ "sha256": "a3484e79abaf3b0b744c0d315580b7f099178ad632d7bc47cb3ceb8222dd4bfd",
+ "width": 340,
+ "height": 758
+ },
+ {
+ "path": "slices/y/64.png",
+ "bytes": 6305,
+ "sha256": "cf331d8be6ab9c3586669755e578db19d583d3b0480f507f6d3a088015521353",
+ "width": 357,
+ "height": 619
+ },
+ {
+ "path": "slices/y/65.png",
+ "bytes": 6620,
+ "sha256": "02f23d4f0ac20bc9b3594ed979bd6a00c185bf5e15f8eab6cbab08cb987b1d3d",
+ "width": 358,
+ "height": 621
+ },
+ {
+ "path": "slices/y/66.png",
+ "bytes": 7008,
+ "sha256": "fd6a62a36d4c9c14dfdc6ea548b13e0de98ff4b54b0a3f7abdf2e341bb6fd5a2",
+ "width": 359,
+ "height": 653
+ },
+ {
+ "path": "slices/y/67.png",
+ "bytes": 7363,
+ "sha256": "cb745df29afc06e41f6198c4eff641d0af05f4733dc145aa866e7effbac1437c",
+ "width": 356,
+ "height": 677
+ },
+ {
+ "path": "slices/y/68.png",
+ "bytes": 7633,
+ "sha256": "baccc4526a1f596cd62c3dbafcd7840b25cd515ca27b3e7b3f96268ddbee0751",
+ "width": 357,
+ "height": 592
+ },
+ {
+ "path": "slices/y/69.png",
+ "bytes": 7945,
+ "sha256": "d1b881ffae27a4e94e33f88b436fe46a2055478fd3709504784af6138e1002ee",
+ "width": 351,
+ "height": 592
+ },
+ {
+ "path": "slices/y/70.png",
+ "bytes": 8039,
+ "sha256": "6b65e14789a625ffd0c9a77fdce49ad3b5484ec426c510e5fa011c1c91166140",
+ "width": 361,
+ "height": 617
+ },
+ {
+ "path": "slices/y/71.png",
+ "bytes": 8010,
+ "sha256": "1bf5d406cf669f736e54b21f112a574004c338a701c268a8910337ecfa1145a7",
+ "width": 362,
+ "height": 619
+ },
+ {
+ "path": "slices/y/72.png",
+ "bytes": 7909,
+ "sha256": "05c9b45e85c3840c079a3b981d7bdab2634cc482ab5e3ba331880b06745e339c",
+ "width": 361,
+ "height": 618
+ },
+ {
+ "path": "slices/y/73.png",
+ "bytes": 8369,
+ "sha256": "536240b03aa2fd4cc17d178508bfa1bc20ebf81f3b48506437df3e4d5f529971",
+ "width": 352,
+ "height": 595
+ },
+ {
+ "path": "slices/y/74.png",
+ "bytes": 8208,
+ "sha256": "aba6d28e28cabf893c417d03bf20e911f0408b9ce1181cfcd25c7aa4aaefcd9d",
+ "width": 352,
+ "height": 513
+ },
+ {
+ "path": "slices/y/75.png",
+ "bytes": 8598,
+ "sha256": "033cf2d60b816793207e1f4b6bc8d28cb68b9d97b65ba0e1e71c497b6ed73555",
+ "width": 348,
+ "height": 570
+ },
+ {
+ "path": "slices/y/76.png",
+ "bytes": 8928,
+ "sha256": "b7c013cad06ae7a5c9a856fe3dc7d0b220dad793c3c76093ede2012bf93e6952",
+ "width": 353,
+ "height": 571
+ },
+ {
+ "path": "slices/y/77.png",
+ "bytes": 9006,
+ "sha256": "6f2d20a9418fb5399647bcf25ed3302e484b2f2e094a2cf9decf9ae678b88941",
+ "width": 352,
+ "height": 516
+ },
+ {
+ "path": "slices/y/78.png",
+ "bytes": 9060,
+ "sha256": "920df1841b744650523d79514d71de2ca9da433f2e66954cc4a70b08ea79899d",
+ "width": 361,
+ "height": 495
+ },
+ {
+ "path": "slices/y/79.png",
+ "bytes": 9268,
+ "sha256": "b14c1256244b3b29cf3784f5c1a11a3e778e44cc44b75165abd6b5f9596032e3",
+ "width": 362,
+ "height": 481
+ },
+ {
+ "path": "slices/y/80.png",
+ "bytes": 9679,
+ "sha256": "2596887f8d962de82016972993cc20a068129b8cfcd24868f8ac78445adad00e",
+ "width": 358,
+ "height": 503
+ },
+ {
+ "path": "slices/y/81.png",
+ "bytes": 10167,
+ "sha256": "259cd41384935a652577468aea3cb6bf1ed23cd3d200c2e2b047af61053ceb61",
+ "width": 352,
+ "height": 558
+ },
+ {
+ "path": "slices/y/82.png",
+ "bytes": 10007,
+ "sha256": "f92d2767eeafc259089319ed5e22ffe4d6727c38d3dbd6e645f986859a4ae962",
+ "width": 333,
+ "height": 563
+ },
+ {
+ "path": "slices/y/83.png",
+ "bytes": 10236,
+ "sha256": "68c35822606f49b3126053cfee3b359aa3c9251f7b3ddb4d3b4f8aa0a1f85960",
+ "width": 353,
+ "height": 470
+ },
+ {
+ "path": "slices/y/84.png",
+ "bytes": 10865,
+ "sha256": "9b1ec5ca7258a9cee6050e5c44f7c9734b67b2c1f66c566f7613542a93c435c8",
+ "width": 356,
+ "height": 525
+ },
+ {
+ "path": "slices/y/85.png",
+ "bytes": 11009,
+ "sha256": "791954884d7a410ac25bb81d7e7e184ef0f4787ace7f9af1241b278ee501f248",
+ "width": 353,
+ "height": 527
+ },
+ {
+ "path": "slices/y/86.png",
+ "bytes": 11070,
+ "sha256": "3d135fcad89d5342b78315ef0f86613e61ab20509b20d71ad040d1d931b636fa",
+ "width": 342,
+ "height": 508
+ },
+ {
+ "path": "slices/y/87.png",
+ "bytes": 12045,
+ "sha256": "06a0fe140d32d991d1e0b241c868b32dd9d1af001ce4928c883a5f07a62b121a",
+ "width": 359,
+ "height": 593
+ },
+ {
+ "path": "slices/y/88.png",
+ "bytes": 12075,
+ "sha256": "5923eed377135f4a996a088aabd433373203ac388866d9a99ea1ef7b224be390",
+ "width": 358,
+ "height": 505
+ },
+ {
+ "path": "slices/y/89.png",
+ "bytes": 12199,
+ "sha256": "32eb9864463985afe09acb9fac3e3e51209e1445b3091aadbcb71031c371a522",
+ "width": 347,
+ "height": 503
+ },
+ {
+ "path": "slices/y/90.png",
+ "bytes": 13212,
+ "sha256": "d5eb503d80fa3f64a479157b53a8931d0b0067e71ef7c9bb9f8a969362927b55",
+ "width": 351,
+ "height": 659
+ },
+ {
+ "path": "slices/y/91.png",
+ "bytes": 13070,
+ "sha256": "890266876cca236c74668b94515d777485120fff5b2a68a34ee5b67324b4fce5",
+ "width": 352,
+ "height": 495
+ },
+ {
+ "path": "slices/y/92.png",
+ "bytes": 13425,
+ "sha256": "23b836049939f253ee0553971309a310a67b664e253d7658f027f97798547038",
+ "width": 357,
+ "height": 509
+ },
+ {
+ "path": "slices/y/93.png",
+ "bytes": 13687,
+ "sha256": "39bba62a6c02b3f4d08be05f44dba9c903d54e243738b166a322b3dc2c1a052d",
+ "width": 351,
+ "height": 509
+ },
+ {
+ "path": "slices/y/94.png",
+ "bytes": 14021,
+ "sha256": "5e9238a4368e725a1054a71228e9094fcb0cea707827f780a8a91d2d5c9824fd",
+ "width": 351,
+ "height": 486
+ },
+ {
+ "path": "slices/y/95.png",
+ "bytes": 13827,
+ "sha256": "3e7a349e0c32625c35b2b30027aa215adfabb73c19a54bdb8e1a40821475289a",
+ "width": 352,
+ "height": 358
+ },
+ {
+ "path": "slices/y/96.png",
+ "bytes": 14389,
+ "sha256": "5a9532475dfcccf32612b4d05ebbaae360e20e0df6249821ea3cf7a134deb18c",
+ "width": 351,
+ "height": 389
+ },
+ {
+ "path": "slices/y/97.png",
+ "bytes": 14769,
+ "sha256": "8c2cf18c334ced48e5eac0fd1ae26a166111726cc6c92aca81ec1ce5d82640ca",
+ "width": 348,
+ "height": 378
+ },
+ {
+ "path": "slices/y/98.png",
+ "bytes": 14681,
+ "sha256": "6ea5cc842796eaf59359c8b8c916658af5c9ee78467c943da1eec40c3268762c",
+ "width": 351,
+ "height": 344
+ },
+ {
+ "path": "slices/y/99.png",
+ "bytes": 15473,
+ "sha256": "e5ba527d49a10804437a1cc04703828f32c9084c7d3d764cc66c83d4da45a8cf",
+ "width": 351,
+ "height": 481
+ },
+ {
+ "path": "slices/z/00.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/01.png",
+ "bytes": 114,
+ "sha256": "847e22ebc7f7ff670f1cd72c57aa485fcf5a1ec50d2041a14b0e1ee2346d5453",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/02.png",
+ "bytes": 94,
+ "sha256": "9c25a1889ab983cbd62789e419111838196ceac0de2630f76adb1d0110bc46b0",
+ "width": 1,
+ "height": 2
+ },
+ {
+ "path": "slices/z/03.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/04.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/05.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/06.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/07.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/08.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/09.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/10.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/100.png",
+ "bytes": 114,
+ "sha256": "e0e7fffa98ea5f08400673dff8fe21128f7f04fe8b46d2f1ab54e22158cd207e",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/101.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/102.png",
+ "bytes": 265,
+ "sha256": "563268da3236343d0e312dabe3e758ace865881df7dee2e14d3f6f528645f809",
+ "width": 224,
+ "height": 94
+ },
+ {
+ "path": "slices/z/103.png",
+ "bytes": 287,
+ "sha256": "c6076098e127382fc7f306a6dcec37271f6b73ed80dd5f6d8f47d8816f9d445c",
+ "width": 224,
+ "height": 94
+ },
+ {
+ "path": "slices/z/104.png",
+ "bytes": 117,
+ "sha256": "9ac4ac472eaac078aaeeae0993ad73564e7053f5f20ef36ca37c9bafcf8f7fcc",
+ "width": 4,
+ "height": 4
+ },
+ {
+ "path": "slices/z/105.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/106.png",
+ "bytes": 116,
+ "sha256": "fe8b5d3fb11e3bd3ed1d98c0a044281f276e0b2ab2939ff98eaa395cd5100bb6",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/107.png",
+ "bytes": 114,
+ "sha256": "bbd49ba99eec2585761ba0e6314e56b00b225dcac1babaa237d4deb4defa9262",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/108.png",
+ "bytes": 102,
+ "sha256": "4898780f7a10241a8b7458140e66cf66dfd8e4bf82702fac321a7572c5d9a3b0",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/109.png",
+ "bytes": 104,
+ "sha256": "1eb3f8565dc7d94ded50d53f5a2fd03be19f265d21261f7329938881708c8d8c",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/11.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/110.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/111.png",
+ "bytes": 214,
+ "sha256": "8e7b9175f0d3a1201685281c86c7f39553baec6bcbf06690a1ca28f1abbaa124",
+ "width": 98,
+ "height": 228
+ },
+ {
+ "path": "slices/z/112.png",
+ "bytes": 106,
+ "sha256": "11a9fd44c9f3b2b3611c8883febd25be1fc53bf689ebf005822cc0c540146830",
+ "width": 3,
+ "height": 2
+ },
+ {
+ "path": "slices/z/113.png",
+ "bytes": 143,
+ "sha256": "036afebde5592af92fef2d046313fceeba62c9253a3638ed277d0a22870e2829",
+ "width": 25,
+ "height": 51
+ },
+ {
+ "path": "slices/z/114.png",
+ "bytes": 277,
+ "sha256": "92a91a6d8723d94dcd85786a4613c790ae4c99327377ce367acc0159c050eacf",
+ "width": 161,
+ "height": 147
+ },
+ {
+ "path": "slices/z/115.png",
+ "bytes": 121,
+ "sha256": "6571bd2581ef20debd600823f8d136e70b1b9084c48030a6878df77c2203ce28",
+ "width": 23,
+ "height": 50
+ },
+ {
+ "path": "slices/z/116.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/117.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/118.png",
+ "bytes": 340,
+ "sha256": "d675ae9cbe7a6d0b03011a259f3064de41bc549ec865455db0a109dc6e61cea5",
+ "width": 200,
+ "height": 275
+ },
+ {
+ "path": "slices/z/119.png",
+ "bytes": 513,
+ "sha256": "820221541e941a6e515acadda5230ed0365a014b5dfac4d85102ded06940319e",
+ "width": 317,
+ "height": 275
+ },
+ {
+ "path": "slices/z/12.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/120.png",
+ "bytes": 531,
+ "sha256": "f15e6cee7bc5f35e91555bcdbbbe35a2ccb4b8eacaf5a58a4f1fb5ebbae9fea7",
+ "width": 317,
+ "height": 301
+ },
+ {
+ "path": "slices/z/121.png",
+ "bytes": 267,
+ "sha256": "b520b703ec074e18edea197eb2aa22a38993fea2841edf640cad72efc44eff2f",
+ "width": 131,
+ "height": 236
+ },
+ {
+ "path": "slices/z/122.png",
+ "bytes": 203,
+ "sha256": "08c1630b28cc77429021a9c97c84dd9c5ee030d6d16e78954d13d39673654279",
+ "width": 209,
+ "height": 41
+ },
+ {
+ "path": "slices/z/123.png",
+ "bytes": 227,
+ "sha256": "3b0668856277d332c141a36e4eac9b2196ea1ca1e7a918eab55620af4316d0e9",
+ "width": 121,
+ "height": 20
+ },
+ {
+ "path": "slices/z/124.png",
+ "bytes": 256,
+ "sha256": "4014ea48f53ffc938f41b008ab5e8c230f79071342f6cdce5464ea0ee835c8ff",
+ "width": 123,
+ "height": 33
+ },
+ {
+ "path": "slices/z/125.png",
+ "bytes": 495,
+ "sha256": "7d24c88b83f8d9983b617fc52e22b85f65d4063e58e72932192a765a07cc09c9",
+ "width": 215,
+ "height": 271
+ },
+ {
+ "path": "slices/z/126.png",
+ "bytes": 514,
+ "sha256": "72a0605084c08d11b913c1bb0989aaf398bc17d8f33f85b7641829218ceef923",
+ "width": 217,
+ "height": 272
+ },
+ {
+ "path": "slices/z/127.png",
+ "bytes": 314,
+ "sha256": "ba4b061442383e517c4eba05d6e72819738a2e85f2598bd7fc9aad56a3ad4a6f",
+ "width": 217,
+ "height": 34
+ },
+ {
+ "path": "slices/z/128.png",
+ "bytes": 293,
+ "sha256": "dae0ec1361125cdcbec944ad5f978330bef42853d84382c2741c95ef8cb94792",
+ "width": 218,
+ "height": 34
+ },
+ {
+ "path": "slices/z/129.png",
+ "bytes": 308,
+ "sha256": "9c0e3d4c0f000865828565d4102dd7be8483ff10a599e0d792ab245eee7e03ba",
+ "width": 126,
+ "height": 48
+ },
+ {
+ "path": "slices/z/13.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/130.png",
+ "bytes": 501,
+ "sha256": "b4cea5b3761c831b24083226a28992b28c2872f4305e09e6a0494c57108607a2",
+ "width": 154,
+ "height": 322
+ },
+ {
+ "path": "slices/z/131.png",
+ "bytes": 348,
+ "sha256": "9070f00dfd151972679450af74f1919fb6e1e41dd92b36dff43febba1c1653c1",
+ "width": 65,
+ "height": 312
+ },
+ {
+ "path": "slices/z/132.png",
+ "bytes": 286,
+ "sha256": "ba865f862037b18aba5d72726082751de724d62aa2a24491f4c0e0cfbddb4b83",
+ "width": 182,
+ "height": 40
+ },
+ {
+ "path": "slices/z/133.png",
+ "bytes": 258,
+ "sha256": "e15ffdbdf1669ceb1971cf7bf05a761ed95785d3142bcb34511a28f6d2ddeffe",
+ "width": 182,
+ "height": 40
+ },
+ {
+ "path": "slices/z/134.png",
+ "bytes": 356,
+ "sha256": "ae0e395fa751a909e5924fe9f0900d1949347d52a58d92915af2d5c88136c9ef",
+ "width": 165,
+ "height": 180
+ },
+ {
+ "path": "slices/z/135.png",
+ "bytes": 355,
+ "sha256": "9c5720c297a565a7b69cf12de9600ef04e5ab1ac8de7b8d93605caac2211f93c",
+ "width": 165,
+ "height": 181
+ },
+ {
+ "path": "slices/z/136.png",
+ "bytes": 281,
+ "sha256": "d143b6de5c5c131a0b34119280ba7031134e2a839ae45d7ed1e6251d12e91a27",
+ "width": 140,
+ "height": 153
+ },
+ {
+ "path": "slices/z/137.png",
+ "bytes": 603,
+ "sha256": "e46479c8e4968cac064b1186b53491b6de1ad7dcdc0b43188a1648e98444c2bb",
+ "width": 292,
+ "height": 331
+ },
+ {
+ "path": "slices/z/138.png",
+ "bytes": 729,
+ "sha256": "671ce13ff75aa1e0703be6d7d4f7058c479fe58cb838de11e6a79c29651b5dde",
+ "width": 297,
+ "height": 331
+ },
+ {
+ "path": "slices/z/139.png",
+ "bytes": 649,
+ "sha256": "449cc1abc4d038c9e7b51038497d1df7a094174977f089e3c414512ba2f6a7ab",
+ "width": 296,
+ "height": 328
+ },
+ {
+ "path": "slices/z/14.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/140.png",
+ "bytes": 154,
+ "sha256": "b8c6e1b9da5c0a67f4f6edcce4759b3aeaebe2e721c80f3e8ac3df3c27ce2c9c",
+ "width": 43,
+ "height": 38
+ },
+ {
+ "path": "slices/z/141.png",
+ "bytes": 368,
+ "sha256": "b41cc0898189c243e0801099a625f447d53ef892825b97c2a6f10728da5f89cd",
+ "width": 232,
+ "height": 247
+ },
+ {
+ "path": "slices/z/142.png",
+ "bytes": 386,
+ "sha256": "f1dd4980abff5fcff9555ad1fddd9a13b976a5fc1ebaec462bb82be139f692ba",
+ "width": 232,
+ "height": 253
+ },
+ {
+ "path": "slices/z/143.png",
+ "bytes": 456,
+ "sha256": "4cf9759f4a9ecc1acbe80ccf6a64af6e68065aa8ffdd61483fade45b905bce5c",
+ "width": 230,
+ "height": 307
+ },
+ {
+ "path": "slices/z/144.png",
+ "bytes": 413,
+ "sha256": "c54b29955469eb8fd4ff37274b7da35b3d9ecfe57e88a646227851166126afd5",
+ "width": 212,
+ "height": 315
+ },
+ {
+ "path": "slices/z/145.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/146.png",
+ "bytes": 184,
+ "sha256": "1bb44ad983165273aed3ee517cec2c3b78fcbbde962a0fd6ccb10e4c51b1a1d0",
+ "width": 77,
+ "height": 193
+ },
+ {
+ "path": "slices/z/147.png",
+ "bytes": 410,
+ "sha256": "f4b00042c7c0ef2b9c63b1c30ef0dfcb7f7c51c14bc3b479da41f94b64209f8c",
+ "width": 294,
+ "height": 212
+ },
+ {
+ "path": "slices/z/148.png",
+ "bytes": 188,
+ "sha256": "00aa85e6b199e875db19036996e4c60d6f0f9e5bb4d90c435ff53e0849f33e38",
+ "width": 38,
+ "height": 277
+ },
+ {
+ "path": "slices/z/149.png",
+ "bytes": 278,
+ "sha256": "ec9eb8288809a8374f940e649e79108f48858b64eddf7eacf60bf62c26638a08",
+ "width": 148,
+ "height": 231
+ },
+ {
+ "path": "slices/z/15.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/150.png",
+ "bytes": 262,
+ "sha256": "b6b6a29e7d3213bbdf7cb92a1aa44656a9bdeeeb7c3ad03020def97887a77148",
+ "width": 290,
+ "height": 60
+ },
+ {
+ "path": "slices/z/151.png",
+ "bytes": 255,
+ "sha256": "0158ec93fca46429629586f40475cc6f20a8f7fec755897d25827995e5a75ffb",
+ "width": 289,
+ "height": 66
+ },
+ {
+ "path": "slices/z/152.png",
+ "bytes": 383,
+ "sha256": "df05be1ae9b4a55e10ec1bfa8bb963bca8f214ae72cfe63f9e12ed6d535b601a",
+ "width": 287,
+ "height": 217
+ },
+ {
+ "path": "slices/z/153.png",
+ "bytes": 389,
+ "sha256": "d9f4f50b1b1130ff4e16ac4fc8cca839c6655fd676f89f6f2709acb5e8ce8f82",
+ "width": 289,
+ "height": 171
+ },
+ {
+ "path": "slices/z/154.png",
+ "bytes": 340,
+ "sha256": "8b2bfcba7be718325a7ca7a765990d7adf891fcf3312548771473a72ad635422",
+ "width": 288,
+ "height": 171
+ },
+ {
+ "path": "slices/z/155.png",
+ "bytes": 147,
+ "sha256": "a51f10b5bf4642361b9ce9cfe5f045d6a5d47ccc0b507b5004e564feba5bcf7b",
+ "width": 25,
+ "height": 17
+ },
+ {
+ "path": "slices/z/156.png",
+ "bytes": 479,
+ "sha256": "2819016f889204112686b9fb7d79571c89a5c8e4421fc360999362c9d1c7551d",
+ "width": 265,
+ "height": 196
+ },
+ {
+ "path": "slices/z/157.png",
+ "bytes": 648,
+ "sha256": "68ae56253ca02c41edf8b96894850f787df5e31d3deaef7a61a859dd12dee59b",
+ "width": 268,
+ "height": 265
+ },
+ {
+ "path": "slices/z/158.png",
+ "bytes": 628,
+ "sha256": "4ca1b5df202ac806501ece0bbc8883e773c18a3ccf93748acac780082510f6b8",
+ "width": 274,
+ "height": 266
+ },
+ {
+ "path": "slices/z/159.png",
+ "bytes": 339,
+ "sha256": "f30264a40f5b27f5b7c7ec10893ed2fd4a790486c79129c1e651da4c3876558f",
+ "width": 142,
+ "height": 67
+ },
+ {
+ "path": "slices/z/16.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/160.png",
+ "bytes": 334,
+ "sha256": "473f8a6563660a4d90e60798bb9cf2202deaff6a925ab3cdbc292c00f69b0e40",
+ "width": 125,
+ "height": 70
+ },
+ {
+ "path": "slices/z/161.png",
+ "bytes": 291,
+ "sha256": "852f8b10dc16488f7ca2d0c8da75bd519afe9751ba45e7dc387392e3d22ba250",
+ "width": 124,
+ "height": 68
+ },
+ {
+ "path": "slices/z/162.png",
+ "bytes": 402,
+ "sha256": "d230493fef9819505da37f41fd11f99e418c2228ad40fcac9f40b30d15ddfd07",
+ "width": 295,
+ "height": 61
+ },
+ {
+ "path": "slices/z/163.png",
+ "bytes": 790,
+ "sha256": "731c208c8c17f8eed6e9c2d8b057d7db5e764899adf15872f597c99182241a01",
+ "width": 296,
+ "height": 337
+ },
+ {
+ "path": "slices/z/164.png",
+ "bytes": 808,
+ "sha256": "d29d64b04e3108c4924bf1946a13b8c6f196d35d7396f398d49294867a1a0569",
+ "width": 296,
+ "height": 337
+ },
+ {
+ "path": "slices/z/165.png",
+ "bytes": 718,
+ "sha256": "57d8a21140fabad8fdd7ab7afe1365af736919244eac705e71a9377766b0c9fc",
+ "width": 285,
+ "height": 336
+ },
+ {
+ "path": "slices/z/166.png",
+ "bytes": 644,
+ "sha256": "982e0ee0e69add44cf6e7b8b96b5432ae319d198b06fd71e838dcacb82fd07bf",
+ "width": 284,
+ "height": 318
+ },
+ {
+ "path": "slices/z/167.png",
+ "bytes": 190,
+ "sha256": "1848aa268ec7ec95b25fa82f86abc5b5db3e0f6cd5f64d13368cb955bf69994e",
+ "width": 39,
+ "height": 39
+ },
+ {
+ "path": "slices/z/168.png",
+ "bytes": 175,
+ "sha256": "1d36bdf2382ce780ee5cd76be195c31d1e363c8e4a1b2a84f1c423bcc067ddda",
+ "width": 40,
+ "height": 40
+ },
+ {
+ "path": "slices/z/169.png",
+ "bytes": 206,
+ "sha256": "bab71c6e799b94c099c2474dd4bf76b8a919e43acb08ce36e0aceca1aecb4b82",
+ "width": 40,
+ "height": 150
+ },
+ {
+ "path": "slices/z/17.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/170.png",
+ "bytes": 361,
+ "sha256": "f727dfe8d516508eff2d72f5e9687fc8fae6f3e096717ff6aefc63205ef2c421",
+ "width": 101,
+ "height": 320
+ },
+ {
+ "path": "slices/z/171.png",
+ "bytes": 625,
+ "sha256": "c8cb94c33432ba1277f35523bc0195cc123abf55235c0e3465434ad0ee54abb6",
+ "width": 293,
+ "height": 320
+ },
+ {
+ "path": "slices/z/172.png",
+ "bytes": 718,
+ "sha256": "25322a83b9072baf9ac262423aa428bd92aed82cf0ff0b6b6ec12b345ce85783",
+ "width": 335,
+ "height": 329
+ },
+ {
+ "path": "slices/z/173.png",
+ "bytes": 721,
+ "sha256": "645d0a083dfcdc842258e2d877ac9c4ad23bfba68ef099f602720e858bd1b5d3",
+ "width": 335,
+ "height": 320
+ },
+ {
+ "path": "slices/z/174.png",
+ "bytes": 564,
+ "sha256": "189d6cc686df4ea90648d45ec95fbc50f91e970234ccdbe8e16133f080ddff1e",
+ "width": 331,
+ "height": 233
+ },
+ {
+ "path": "slices/z/175.png",
+ "bytes": 438,
+ "sha256": "6f06992b4deee907f0b75885d8d42f32c9917b95d2ca32f9e4686aeec74037e8",
+ "width": 284,
+ "height": 234
+ },
+ {
+ "path": "slices/z/176.png",
+ "bytes": 508,
+ "sha256": "8ea4ba4b16b640b6cb693b74dbd99a2acea8275742fecb3edf6f52596cbee9a4",
+ "width": 341,
+ "height": 219
+ },
+ {
+ "path": "slices/z/177.png",
+ "bytes": 515,
+ "sha256": "687eea67f95b51fd9db911ce0972af0a4ca97ac51fb77e9ff6340a61a23ac587",
+ "width": 257,
+ "height": 228
+ },
+ {
+ "path": "slices/z/178.png",
+ "bytes": 673,
+ "sha256": "f7208f9e76abeab9ebd3bc4435ef7e4659f4035f3f8f713c75624c7e7cf3641f",
+ "width": 318,
+ "height": 297
+ },
+ {
+ "path": "slices/z/179.png",
+ "bytes": 731,
+ "sha256": "534da440a9a9a2b7b44faef774e7edbc1c5ce65ff1c4c2682deefded035370a4",
+ "width": 326,
+ "height": 298
+ },
+ {
+ "path": "slices/z/18.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/180.png",
+ "bytes": 711,
+ "sha256": "d8f2ee8da53ca9daee9d9c684aedff4c9b1005c5738909e5edd302be0bd735f6",
+ "width": 327,
+ "height": 305
+ },
+ {
+ "path": "slices/z/181.png",
+ "bytes": 722,
+ "sha256": "bb47e66e87a79f33db84e0a604db5a1eeb517b77cc6f9a836f9e6911a8de3fbe",
+ "width": 335,
+ "height": 304
+ },
+ {
+ "path": "slices/z/182.png",
+ "bytes": 751,
+ "sha256": "09f43619c3e43ec5961005a674662ddc262a5c47400cefb07163c9ac08d25943",
+ "width": 318,
+ "height": 295
+ },
+ {
+ "path": "slices/z/183.png",
+ "bytes": 633,
+ "sha256": "367ce3bbe7b430792b03697ad7a6ddbe0dff51a176465ac0380c30e228c3a8d9",
+ "width": 260,
+ "height": 295
+ },
+ {
+ "path": "slices/z/184.png",
+ "bytes": 619,
+ "sha256": "34a20ed90d0e8784bdd478a39be3f37833c811d0d7fd94e11e636f9897a11d5a",
+ "width": 228,
+ "height": 294
+ },
+ {
+ "path": "slices/z/185.png",
+ "bytes": 699,
+ "sha256": "d344ad4142089bb1b14a675da30c2fd5f79dcd3d0be4da389dee0bdf0dace6b1",
+ "width": 261,
+ "height": 270
+ },
+ {
+ "path": "slices/z/186.png",
+ "bytes": 623,
+ "sha256": "4094301f789629d2dcaf674640f1eb30101bfdfde5318be2d7415d1171fb3b37",
+ "width": 268,
+ "height": 273
+ },
+ {
+ "path": "slices/z/187.png",
+ "bytes": 833,
+ "sha256": "60dfeb9f6bc71bfebc7ef6ed7b53e52b05c4a6919cb274e3c59e29ddaa6d46c0",
+ "width": 309,
+ "height": 335
+ },
+ {
+ "path": "slices/z/188.png",
+ "bytes": 1060,
+ "sha256": "ce35ad00e8a8501818f127e9724cdf989698cac2f749a41adc93254c1dcb3244",
+ "width": 343,
+ "height": 337
+ },
+ {
+ "path": "slices/z/189.png",
+ "bytes": 1101,
+ "sha256": "4bcc496a8d3a4b527b0a1f5616ff9f09198ed0bddd78b951f4677430e0900474",
+ "width": 343,
+ "height": 356
+ },
+ {
+ "path": "slices/z/19.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/190.png",
+ "bytes": 1090,
+ "sha256": "5b8d9336f5a92f48df99270aa2bb1b0b555772713b3f24176ac9d0748a1446ff",
+ "width": 302,
+ "height": 356
+ },
+ {
+ "path": "slices/z/191.png",
+ "bytes": 1057,
+ "sha256": "3f93b32d46f95690c69e62b0f8e01f946f94b7671bb03eb362734b58a4560c7b",
+ "width": 318,
+ "height": 347
+ },
+ {
+ "path": "slices/z/192.png",
+ "bytes": 1125,
+ "sha256": "5e51d72912e373f80c9553f33344b2f24ef94e79a15c187ec84b195cfe8ab41f",
+ "width": 335,
+ "height": 329
+ },
+ {
+ "path": "slices/z/193.png",
+ "bytes": 1127,
+ "sha256": "e277b3f9387daa01eb0a6f356fedca6a8b33703f2b1b2f553bd34e802bcc5c1e",
+ "width": 337,
+ "height": 314
+ },
+ {
+ "path": "slices/z/194.png",
+ "bytes": 1145,
+ "sha256": "508d01f27b88b48de83504df7572d1ece74913ef25a32d23eee1b3443382c1d7",
+ "width": 342,
+ "height": 315
+ },
+ {
+ "path": "slices/z/195.png",
+ "bytes": 1305,
+ "sha256": "0774a469aa134b70bb0187e3fdb78f3a54c1708712238d530b5932a4f91f0415",
+ "width": 331,
+ "height": 333
+ },
+ {
+ "path": "slices/z/196.png",
+ "bytes": 1336,
+ "sha256": "c4e3ffc7fbb93abbf84421d98ae413c11da2608d9e9567baaa56ff135e9625ea",
+ "width": 347,
+ "height": 341
+ },
+ {
+ "path": "slices/z/197.png",
+ "bytes": 1499,
+ "sha256": "3079862bccf9cde9cf675265131814f018f084dab7eb9e04014e7f5caf8ac48d",
+ "width": 361,
+ "height": 341
+ },
+ {
+ "path": "slices/z/198.png",
+ "bytes": 1642,
+ "sha256": "455c034d3a94f4499d47f2433021cc70c1f8d9c1c5d6c01e3d115e8c9f250856",
+ "width": 362,
+ "height": 343
+ },
+ {
+ "path": "slices/z/199.png",
+ "bytes": 1999,
+ "sha256": "d8f4c07c6a558c21f08020cd970f6c3b5c3f14e94da76d7e3d75d584c72c47b6",
+ "width": 341,
+ "height": 312
+ },
+ {
+ "path": "slices/z/20.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/200.png",
+ "bytes": 2592,
+ "sha256": "d0f500ee64e2e417e5f56c52787f919cbb7860064729c2f469166f239570a238",
+ "width": 366,
+ "height": 340
+ },
+ {
+ "path": "slices/z/201.png",
+ "bytes": 3465,
+ "sha256": "89ba3aaf177c40084c3aa8ef1e706e374a57903b4a52e4b4d1dc619ae4bc8f47",
+ "width": 366,
+ "height": 339
+ },
+ {
+ "path": "slices/z/202.png",
+ "bytes": 3881,
+ "sha256": "de120802be0876cba0917a3aa225b3d02dc7bad00f5717170e0af8434fefbc38",
+ "width": 348,
+ "height": 334
+ },
+ {
+ "path": "slices/z/203.png",
+ "bytes": 3891,
+ "sha256": "0f9bf45460bc90cbe79ebb6a25d12f309c365730d11e9e4f621f2ae9f4edd0d5",
+ "width": 356,
+ "height": 353
+ },
+ {
+ "path": "slices/z/204.png",
+ "bytes": 4060,
+ "sha256": "66f42c61b2f32200643d1ede6241871b39defe4ed212d2cccb25c3cf21a5ac70",
+ "width": 357,
+ "height": 354
+ },
+ {
+ "path": "slices/z/205.png",
+ "bytes": 4177,
+ "sha256": "dda0b47df7888921cff1d617e36284209d46b6c5e709669cded3cd4706c1f634",
+ "width": 356,
+ "height": 324
+ },
+ {
+ "path": "slices/z/206.png",
+ "bytes": 4131,
+ "sha256": "cb9e63156e53de249a29d6a386c4fa566252271ad59d514d3775868e843de5bd",
+ "width": 352,
+ "height": 352
+ },
+ {
+ "path": "slices/z/207.png",
+ "bytes": 4318,
+ "sha256": "1db8ab4447eba57cb841c4ba63643b7e159c8a4e9135892d4e1ee2cc20597d5e",
+ "width": 363,
+ "height": 352
+ },
+ {
+ "path": "slices/z/208.png",
+ "bytes": 4302,
+ "sha256": "cb8017830f123f66260fdd457730da9dc1294514fc9e2255780c623e8ab1e5ac",
+ "width": 364,
+ "height": 331
+ },
+ {
+ "path": "slices/z/209.png",
+ "bytes": 4353,
+ "sha256": "9a5d397c8680fed545ebdc30501b7fa98a559762529205a48ab96619dea8a6e0",
+ "width": 364,
+ "height": 351
+ },
+ {
+ "path": "slices/z/21.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/210.png",
+ "bytes": 4352,
+ "sha256": "d7c21fdd66b1bc00dd7b76de83f20808081d003173282091b02f9ccf82b3f5bd",
+ "width": 351,
+ "height": 349
+ },
+ {
+ "path": "slices/z/211.png",
+ "bytes": 4649,
+ "sha256": "e06af0c3dcc647153bf8671566a249110a1e96addf0bb7e9c4c8d8ba34ee5603",
+ "width": 359,
+ "height": 355
+ },
+ {
+ "path": "slices/z/212.png",
+ "bytes": 5157,
+ "sha256": "5592453d9736dd57378f4960c8f6ce14617840000d862b47e4ded96087f348b5",
+ "width": 358,
+ "height": 353
+ },
+ {
+ "path": "slices/z/213.png",
+ "bytes": 5510,
+ "sha256": "1c90dd908b073058c26f31240d21507f7b50298cb99d247b9daf2b6e62ce7dda",
+ "width": 358,
+ "height": 352
+ },
+ {
+ "path": "slices/z/214.png",
+ "bytes": 5812,
+ "sha256": "69946ce01e1d19a79f5755f471915694bb094763095b86112dade21f3e3aec47",
+ "width": 361,
+ "height": 349
+ },
+ {
+ "path": "slices/z/215.png",
+ "bytes": 6054,
+ "sha256": "55d224774ea8837b56a09c9822f035df0ba9588534d631ccab90d09dd97a9f13",
+ "width": 358,
+ "height": 352
+ },
+ {
+ "path": "slices/z/216.png",
+ "bytes": 6710,
+ "sha256": "7e23003e4052ca1e1f012562c8a4f0c1210f1f32ac62b21ceb555a259ad1ef5c",
+ "width": 364,
+ "height": 352
+ },
+ {
+ "path": "slices/z/217.png",
+ "bytes": 6835,
+ "sha256": "17ea96eead22282115d63b2d46d1c015617219ba71456b3f7eb002a0c6eea0c5",
+ "width": 360,
+ "height": 350
+ },
+ {
+ "path": "slices/z/218.png",
+ "bytes": 7054,
+ "sha256": "6b89672a19076dbd41a4a5b8b08a606d444fd78b9c1b78c8662f4c1207fd02c9",
+ "width": 356,
+ "height": 353
+ },
+ {
+ "path": "slices/z/219.png",
+ "bytes": 7211,
+ "sha256": "03de403621f687ccd4a158541e024b7132f4072f35df036c5d6747d8b833d6b6",
+ "width": 362,
+ "height": 355
+ },
+ {
+ "path": "slices/z/22.png",
+ "bytes": 114,
+ "sha256": "6e54ba94b75b2db34f86403b73f12ff862b69a2867ef8e60f4264a0e3ff6e216",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/220.png",
+ "bytes": 7547,
+ "sha256": "7a91c18edfed5270c701fbb7764267d6ffe7d50a6fb00f98937afa1a03a14a72",
+ "width": 362,
+ "height": 353
+ },
+ {
+ "path": "slices/z/221.png",
+ "bytes": 7803,
+ "sha256": "3943646acffdc3ff66d7cc8e8ee8150c04c094662ad62aad10896402a5bbe48f",
+ "width": 359,
+ "height": 353
+ },
+ {
+ "path": "slices/z/222.png",
+ "bytes": 8001,
+ "sha256": "5be2ccf76a7d7ef6067aad5beb957db20eb88a81e03c8aa2a5be0e206984a2f4",
+ "width": 360,
+ "height": 355
+ },
+ {
+ "path": "slices/z/223.png",
+ "bytes": 8076,
+ "sha256": "251454daa46819063a97f427dd93991f98a8a5152325ad4beebffb294cd6aa69",
+ "width": 361,
+ "height": 353
+ },
+ {
+ "path": "slices/z/224.png",
+ "bytes": 8390,
+ "sha256": "34e84b7f7ae5ee6577b9800aa232f8f24fbcfeda5dec699e38610b90eef08ae0",
+ "width": 360,
+ "height": 354
+ },
+ {
+ "path": "slices/z/225.png",
+ "bytes": 8764,
+ "sha256": "0afd75e78ece9fc443ce94f01aa6f951ad542ed307f01d282ea166ddbe83302a",
+ "width": 361,
+ "height": 358
+ },
+ {
+ "path": "slices/z/226.png",
+ "bytes": 9007,
+ "sha256": "ce18de782e1982d765be0b81f13b15c4eaa167a31c9471c019c6195c42553767",
+ "width": 361,
+ "height": 358
+ },
+ {
+ "path": "slices/z/227.png",
+ "bytes": 8959,
+ "sha256": "e052024f5678b5ff3c2ba3a166e4de1ba558c718a79e5b8723be37f3e6a6adaa",
+ "width": 357,
+ "height": 357
+ },
+ {
+ "path": "slices/z/228.png",
+ "bytes": 9240,
+ "sha256": "223b559cda36ab4543db852e263b07388ce5ffecb3d05eea363deae7ac493be4",
+ "width": 359,
+ "height": 359
+ },
+ {
+ "path": "slices/z/229.png",
+ "bytes": 9622,
+ "sha256": "0264f54fd15862edc8f1b1c20e9f33ac6c5d3f9a9855c76642f3aabc4f9623df",
+ "width": 361,
+ "height": 361
+ },
+ {
+ "path": "slices/z/23.png",
+ "bytes": 94,
+ "sha256": "844834f70fda51dc9ba4ab6c00b780d8374e818997088c5a983d6cfe29f9dcb7",
+ "width": 1,
+ "height": 2
+ },
+ {
+ "path": "slices/z/230.png",
+ "bytes": 9830,
+ "sha256": "a144b9b2a7714410439d247ce61ebe025f6aa2345a4926bbe441775cdf93e379",
+ "width": 359,
+ "height": 363
+ },
+ {
+ "path": "slices/z/231.png",
+ "bytes": 10025,
+ "sha256": "b68076950f06d84e34be6fb9db2ae7f85f063e4bfdf54e1063c3e29038bf990b",
+ "width": 360,
+ "height": 365
+ },
+ {
+ "path": "slices/z/232.png",
+ "bytes": 10414,
+ "sha256": "e77d45e842792167bbc3daa8179d86193272ef5f3f465a34f877f86b05dcc412",
+ "width": 361,
+ "height": 366
+ },
+ {
+ "path": "slices/z/233.png",
+ "bytes": 10741,
+ "sha256": "077bb2bebd6c4f23951101f75ba1204ceedb42d51fb031910d0240c634357cfc",
+ "width": 357,
+ "height": 364
+ },
+ {
+ "path": "slices/z/234.png",
+ "bytes": 10681,
+ "sha256": "04607c77e3750a0f3e7adcfff8a0f39a0f5f014c7edce503b550076340025d6f",
+ "width": 361,
+ "height": 366
+ },
+ {
+ "path": "slices/z/235.png",
+ "bytes": 11108,
+ "sha256": "a4522790288e7c4ab3ea0c66c98fa006b5f0e477671dc4d710b6c4eccb2f11c1",
+ "width": 363,
+ "height": 367
+ },
+ {
+ "path": "slices/z/236.png",
+ "bytes": 11144,
+ "sha256": "e45522def9cf39604d7e479ed12f5a3b00f91b99f2c45e9cf6071299738cfa14",
+ "width": 359,
+ "height": 368
+ },
+ {
+ "path": "slices/z/237.png",
+ "bytes": 11837,
+ "sha256": "3632e0245c8315083e7cb31bdc5e1dc5fafb5c7058ec8513a2dd2937ba341736",
+ "width": 364,
+ "height": 369
+ },
+ {
+ "path": "slices/z/238.png",
+ "bytes": 12028,
+ "sha256": "fb6b47242317fb9b6bda89c49e4f64f89ceab09d427960c04edfad9b6c1723cd",
+ "width": 364,
+ "height": 369
+ },
+ {
+ "path": "slices/z/239.png",
+ "bytes": 12117,
+ "sha256": "1e667e03e6f36a3f4ec50a530707845ad23fff61bea1c147eadf3a3e33503086",
+ "width": 355,
+ "height": 370
+ },
+ {
+ "path": "slices/z/24.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/240.png",
+ "bytes": 12563,
+ "sha256": "c1a062e145188c5c658dd7a45e7bfbf862668b3bda356982c4587d204c64345f",
+ "width": 355,
+ "height": 369
+ },
+ {
+ "path": "slices/z/241.png",
+ "bytes": 13174,
+ "sha256": "e54524925d26a4665d127b8c58fed50407a7d8b6ed0c0bb79762931a2dfb34af",
+ "width": 356,
+ "height": 369
+ },
+ {
+ "path": "slices/z/242.png",
+ "bytes": 13479,
+ "sha256": "da5b0b4dc61405e657bdb653547e420aded2586df993f923804b30ae9b91c91d",
+ "width": 362,
+ "height": 368
+ },
+ {
+ "path": "slices/z/243.png",
+ "bytes": 13730,
+ "sha256": "3d90b41fcec0d685c362f5f4d75f21489c9ce0388bebf3007486023d5efbf29d",
+ "width": 356,
+ "height": 366
+ },
+ {
+ "path": "slices/z/244.png",
+ "bytes": 14349,
+ "sha256": "2e3d87878f7b5415a282248aaf8492efd5879e84fe0bdc460584aa4f78931952",
+ "width": 359,
+ "height": 368
+ },
+ {
+ "path": "slices/z/245.png",
+ "bytes": 14651,
+ "sha256": "94832deac3870fcfcea988948561ac970573cd1ab967b28ab059fd9c8e5a2246",
+ "width": 356,
+ "height": 368
+ },
+ {
+ "path": "slices/z/246.png",
+ "bytes": 14786,
+ "sha256": "6faea90c8d4dc23726517df056a66f2d2b4a63dac8cfde2061a0d329d73bc3a3",
+ "width": 355,
+ "height": 369
+ },
+ {
+ "path": "slices/z/247.png",
+ "bytes": 15189,
+ "sha256": "cc32e8a3b2c95a8276a61f5ba364e1e53d295bf9e7b2a8e40dafb25b7e83b0a3",
+ "width": 356,
+ "height": 364
+ },
+ {
+ "path": "slices/z/248.png",
+ "bytes": 15512,
+ "sha256": "c9801b1039682a262d0626459da71753c7a71bcb2091ea57ece51584cd9c1464",
+ "width": 363,
+ "height": 364
+ },
+ {
+ "path": "slices/z/249.png",
+ "bytes": 15841,
+ "sha256": "885dcd7c17922996ec33cd2391a6296f723f84e01ee82011ba0506467cc18ab6",
+ "width": 357,
+ "height": 360
+ },
+ {
+ "path": "slices/z/25.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/250.png",
+ "bytes": 16029,
+ "sha256": "43054f3a20105487a98422a6cb108a883524684d0679f1e24b14daf8e802e344",
+ "width": 359,
+ "height": 363
+ },
+ {
+ "path": "slices/z/251.png",
+ "bytes": 16270,
+ "sha256": "fb58ad8867eca0b02b6e1c151c84057269f0900fd22762c4819a9e995abb05aa",
+ "width": 359,
+ "height": 356
+ },
+ {
+ "path": "slices/z/252.png",
+ "bytes": 16512,
+ "sha256": "1aaa623d5550034e71f333a70efda7e0ec4352cb11e52f3c86e9bad3fd448002",
+ "width": 364,
+ "height": 363
+ },
+ {
+ "path": "slices/z/253.png",
+ "bytes": 16600,
+ "sha256": "94c24b2279ce706d5d59ddf5d231a4ce76be68b880c47eb99a5413fe7128054a",
+ "width": 357,
+ "height": 355
+ },
+ {
+ "path": "slices/z/254.png",
+ "bytes": 16842,
+ "sha256": "0c6ae04af01925ecbecfa47a1da54f303c0bd3876b57b363ab68f5e02448f788",
+ "width": 362,
+ "height": 357
+ },
+ {
+ "path": "slices/z/255.png",
+ "bytes": 17444,
+ "sha256": "6bd83acdc4b34e868dee86d96acbb4470d125c519fceb4bb2a035bb806e0023f",
+ "width": 359,
+ "height": 367
+ },
+ {
+ "path": "slices/z/256.png",
+ "bytes": 17473,
+ "sha256": "0b9f28b7fa12c94c90c5f773da7e3fc3ca6921f5bb614836e1b064d9fad04db6",
+ "width": 365,
+ "height": 358
+ },
+ {
+ "path": "slices/z/257.png",
+ "bytes": 17495,
+ "sha256": "cdc36aefe4577d39f05f1b835fb615faddefee0d6a1a50ad7b888e08bbdcf04c",
+ "width": 359,
+ "height": 359
+ },
+ {
+ "path": "slices/z/258.png",
+ "bytes": 17937,
+ "sha256": "4ae82b33e6f8f1ed45f5730e693fae3826dbc8b7d27fcf3df25f4058c906f16a",
+ "width": 358,
+ "height": 357
+ },
+ {
+ "path": "slices/z/259.png",
+ "bytes": 17968,
+ "sha256": "5a223f716e31d316ccb92cf2ffc0cb1438881a6f581051202ced2a38fdb106e2",
+ "width": 364,
+ "height": 358
+ },
+ {
+ "path": "slices/z/26.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/260.png",
+ "bytes": 17668,
+ "sha256": "0146c02248b689293f5cd7ed64037385b0a8ac3831a1703dbe071ab257c424fd",
+ "width": 357,
+ "height": 356
+ },
+ {
+ "path": "slices/z/261.png",
+ "bytes": 18051,
+ "sha256": "bfcb98f23ac52ef70a1e42832209364603ddd9a99ab03d4a3bfdee5e82007e45",
+ "width": 353,
+ "height": 357
+ },
+ {
+ "path": "slices/z/262.png",
+ "bytes": 18333,
+ "sha256": "c94e316e48442f92630fd0e2d15bd8b08ba8745adde7cc7c11b6583e43030b48",
+ "width": 356,
+ "height": 356
+ },
+ {
+ "path": "slices/z/263.png",
+ "bytes": 18775,
+ "sha256": "9bf2056fa7a9d3f7ad3b66dacdb36fa7a7fcc8a61dddb70bf8cd8c90d1c9148f",
+ "width": 355,
+ "height": 355
+ },
+ {
+ "path": "slices/z/264.png",
+ "bytes": 19040,
+ "sha256": "313e8f901536a1707094ab7bf6f72cd7e7c721718d98e32144f6e7cfaff2b99b",
+ "width": 354,
+ "height": 355
+ },
+ {
+ "path": "slices/z/265.png",
+ "bytes": 19114,
+ "sha256": "8399794efef9ee315099e3e3cb9692995a3c629a892e126b4c491f9bbc3453da",
+ "width": 354,
+ "height": 357
+ },
+ {
+ "path": "slices/z/266.png",
+ "bytes": 18978,
+ "sha256": "d504e68175cfa35fb3760945dbe61e588596682af42f65b7a6a170c728c6a0dc",
+ "width": 354,
+ "height": 357
+ },
+ {
+ "path": "slices/z/267.png",
+ "bytes": 19209,
+ "sha256": "5866fdd94305a1c435999c86a7e6c0d43d21604400ae6d8037d7b853e16121b7",
+ "width": 354,
+ "height": 358
+ },
+ {
+ "path": "slices/z/268.png",
+ "bytes": 19390,
+ "sha256": "a4b4a872ead06a49459ce3fc19cbbe997bfe3a3bd2bf98e3a5f456a0bb58a32f",
+ "width": 361,
+ "height": 357
+ },
+ {
+ "path": "slices/z/269.png",
+ "bytes": 19719,
+ "sha256": "679ef7ca36f62b639292f449ed76a2ea3a8af36584901295bd5b44dcaac7c4c2",
+ "width": 355,
+ "height": 355
+ },
+ {
+ "path": "slices/z/27.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/270.png",
+ "bytes": 19810,
+ "sha256": "b482e5ffdf7ff09496a5ae2c9b9f0587b833a65cb59fd7cba6bba201de75753f",
+ "width": 355,
+ "height": 357
+ },
+ {
+ "path": "slices/z/271.png",
+ "bytes": 19977,
+ "sha256": "272efe886afa35a93be3d9d95e7ce43c748a9347b44500f2aab5e5bb1329e3e0",
+ "width": 355,
+ "height": 359
+ },
+ {
+ "path": "slices/z/272.png",
+ "bytes": 20005,
+ "sha256": "c3f0e3e0e440e54fcb094ca9d7e38e6d0b3ce90dd86c7ffea1e3d2d8f5acad3c",
+ "width": 359,
+ "height": 356
+ },
+ {
+ "path": "slices/z/273.png",
+ "bytes": 19912,
+ "sha256": "02e5047428a2d0fd55295aab94a2d2a63a9b2d8fc62069f0268b1117478a4e68",
+ "width": 355,
+ "height": 356
+ },
+ {
+ "path": "slices/z/274.png",
+ "bytes": 20004,
+ "sha256": "c4cf8b5115217508c41f0ebf00dc692cf2c91f69433ce6d8beca4471ac1d5611",
+ "width": 355,
+ "height": 358
+ },
+ {
+ "path": "slices/z/275.png",
+ "bytes": 20028,
+ "sha256": "87007e8292d4e8dd937a49d67472674f8c3bc733b5bf301e480cd93346241e11",
+ "width": 355,
+ "height": 357
+ },
+ {
+ "path": "slices/z/276.png",
+ "bytes": 20061,
+ "sha256": "54c5d30d13a301b0658431ecd825889cd54b6010eef78d15567bf2acb42b9b5c",
+ "width": 355,
+ "height": 357
+ },
+ {
+ "path": "slices/z/277.png",
+ "bytes": 20077,
+ "sha256": "535b9f9fb58b00b254ae480ec9f51d69906c556a95d06a21f8d19f5b2d86fa0f",
+ "width": 355,
+ "height": 357
+ },
+ {
+ "path": "slices/z/278.png",
+ "bytes": 20093,
+ "sha256": "1057f3cd6c5212ed5dfd68a0edf9c46e97dffc4cfdd2bf01db5c7eb2511bbd5d",
+ "width": 356,
+ "height": 356
+ },
+ {
+ "path": "slices/z/279.png",
+ "bytes": 20147,
+ "sha256": "e8b44aefa243e223752f29a95768410e7759a991a79ed24a54790016ec23f9e2",
+ "width": 358,
+ "height": 354
+ },
+ {
+ "path": "slices/z/28.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/280.png",
+ "bytes": 19838,
+ "sha256": "8b300f9b3771b39a83e49bc8870337dac1235ddd22b1401afd6f326965e77d03",
+ "width": 356,
+ "height": 355
+ },
+ {
+ "path": "slices/z/281.png",
+ "bytes": 19992,
+ "sha256": "3d2ad0680047217376667d2b0709b07cb69d22df76a8295b4cfcd6140a121560",
+ "width": 355,
+ "height": 357
+ },
+ {
+ "path": "slices/z/282.png",
+ "bytes": 19922,
+ "sha256": "4bb638f0d93c415690de74ff21d42d6013620db347cbcc192b4f9b84b41d8726",
+ "width": 358,
+ "height": 357
+ },
+ {
+ "path": "slices/z/283.png",
+ "bytes": 19749,
+ "sha256": "897188e06b9a233778a5fec4f891648e8a58dc4bffa31df45fc374f83c68ee04",
+ "width": 355,
+ "height": 355
+ },
+ {
+ "path": "slices/z/284.png",
+ "bytes": 19446,
+ "sha256": "0b7939161d496e17e9bb25681d9ff03c9a3b40d8c7db91e49597d21b6307b314",
+ "width": 355,
+ "height": 356
+ },
+ {
+ "path": "slices/z/285.png",
+ "bytes": 19476,
+ "sha256": "f33a83210d4eccacf88956c79094b0abdaa7d6c784867243757fdcea4bd2b680",
+ "width": 357,
+ "height": 357
+ },
+ {
+ "path": "slices/z/286.png",
+ "bytes": 19555,
+ "sha256": "bb65e3d8092e9b84598a8f253143a93c97043fb6f3f2e809a69d7a718a1ab73f",
+ "width": 358,
+ "height": 354
+ },
+ {
+ "path": "slices/z/287.png",
+ "bytes": 19290,
+ "sha256": "36cbda4c16704f13647c9d4f7e5cba6f1c0a3389cc811a8373721d873d968a7b",
+ "width": 357,
+ "height": 356
+ },
+ {
+ "path": "slices/z/288.png",
+ "bytes": 19417,
+ "sha256": "2cee86afb39bb041c85307a8421f38a774c16470f2330149cea484894e7c0b50",
+ "width": 359,
+ "height": 357
+ },
+ {
+ "path": "slices/z/289.png",
+ "bytes": 19377,
+ "sha256": "279ca80ae46594cbd61521aa7756d29c47d6ea800d6d52e2d5b126ff16f3c713",
+ "width": 359,
+ "height": 354
+ },
+ {
+ "path": "slices/z/29.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/290.png",
+ "bytes": 19286,
+ "sha256": "3858fee0abb6553112e68f6cd6638e672987443516ca09546324c8c375f5771a",
+ "width": 358,
+ "height": 357
+ },
+ {
+ "path": "slices/z/291.png",
+ "bytes": 18833,
+ "sha256": "1004f8b90032e621ed8c6bf38463580455e50b95b8c915d491c9ed97d30dc6bd",
+ "width": 359,
+ "height": 356
+ },
+ {
+ "path": "slices/z/292.png",
+ "bytes": 19016,
+ "sha256": "b7f496002024a6ceb269281ac39b3d0f1ce3c6f7a620fe5e0e675bf9616726ad",
+ "width": 359,
+ "height": 357
+ },
+ {
+ "path": "slices/z/293.png",
+ "bytes": 18536,
+ "sha256": "54bb1667975abbd6d137720d0ca439dd21e8d47bf9317cf36efde7b442bf636c",
+ "width": 360,
+ "height": 357
+ },
+ {
+ "path": "slices/z/294.png",
+ "bytes": 18452,
+ "sha256": "0fb90d1815dbdec6ab42c5e0cfdc169294775b7b3af3c0f88ec520484c65cb8c",
+ "width": 360,
+ "height": 358
+ },
+ {
+ "path": "slices/z/295.png",
+ "bytes": 18435,
+ "sha256": "32d1d20a7906e1f6cbc116ec939e49eaa1758c7b9cac816c119a292e8b4439f3",
+ "width": 361,
+ "height": 356
+ },
+ {
+ "path": "slices/z/296.png",
+ "bytes": 18074,
+ "sha256": "933e8f1a52ce6537251c4063975450d2e37030a3abfc596b2923b07ae290ec06",
+ "width": 361,
+ "height": 356
+ },
+ {
+ "path": "slices/z/297.png",
+ "bytes": 18116,
+ "sha256": "21a64d1da318b5a156ce9a41427b76998f2914b11a32fd33d0aebf7b7e9e0339",
+ "width": 362,
+ "height": 358
+ },
+ {
+ "path": "slices/z/298.png",
+ "bytes": 18091,
+ "sha256": "3a53415a47f1c0e1e433b570db685c7613489197b35c83ed85e26747be22891c",
+ "width": 361,
+ "height": 357
+ },
+ {
+ "path": "slices/z/299.png",
+ "bytes": 18060,
+ "sha256": "9bb99b759d024689bb909adf763464358fdd4e9cd594640f22764867c3fb4ae4",
+ "width": 361,
+ "height": 362
+ },
+ {
+ "path": "slices/z/30.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/300.png",
+ "bytes": 17665,
+ "sha256": "8c5cd0881e0c9cb211a9595176b1051b50b52a5c060e8cfba3bc5f4be8141ea0",
+ "width": 356,
+ "height": 360
+ },
+ {
+ "path": "slices/z/301.png",
+ "bytes": 17561,
+ "sha256": "639757896d83b31afab53c5e16ac86a95d976844de12ec42bf0ea731eabf5b9b",
+ "width": 361,
+ "height": 357
+ },
+ {
+ "path": "slices/z/302.png",
+ "bytes": 17291,
+ "sha256": "41316fd36e8a889ab564b4df2c9d0ce22af6fef30a8ef41cd48ee79b29328fc2",
+ "width": 362,
+ "height": 356
+ },
+ {
+ "path": "slices/z/303.png",
+ "bytes": 17235,
+ "sha256": "1f8d44c152910f6cc3c8b30b3d1cf36fae350f523e1db49dde7c54f7b35246a8",
+ "width": 362,
+ "height": 362
+ },
+ {
+ "path": "slices/z/304.png",
+ "bytes": 17210,
+ "sha256": "3d8b166dae8e0f98baa19ed60f71d99f1e25ff39692ac4f2845d073c07caef76",
+ "width": 363,
+ "height": 370
+ },
+ {
+ "path": "slices/z/305.png",
+ "bytes": 16835,
+ "sha256": "1fbd0493919fa8fbeaf743c7bb76796843b197c8fde0f83fa66ccfbe6f6de24c",
+ "width": 357,
+ "height": 361
+ },
+ {
+ "path": "slices/z/306.png",
+ "bytes": 16839,
+ "sha256": "4f0414300bce044fd05bc2b6ded65764f9bdb823b817306cadcd7accc94a5399",
+ "width": 354,
+ "height": 357
+ },
+ {
+ "path": "slices/z/307.png",
+ "bytes": 16667,
+ "sha256": "6c5e526b22b785598622a6c2d651352d29fd11344055313c124badd86cdd5477",
+ "width": 362,
+ "height": 359
+ },
+ {
+ "path": "slices/z/308.png",
+ "bytes": 16331,
+ "sha256": "06b51b82a40da983a8d53adca9cc356f3fb06aa53b06c8c80fdba9c715c40a60",
+ "width": 354,
+ "height": 364
+ },
+ {
+ "path": "slices/z/309.png",
+ "bytes": 16146,
+ "sha256": "dcc4de85a7f1a04984dc7b251838574648e8dd2f8313f0cbed0beb8dba006205",
+ "width": 361,
+ "height": 365
+ },
+ {
+ "path": "slices/z/31.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/310.png",
+ "bytes": 15976,
+ "sha256": "00e8cdb00eb5b51b83aa40b3f7524099eb237f48bb5d87c0ad4c9258358d085b",
+ "width": 355,
+ "height": 366
+ },
+ {
+ "path": "slices/z/311.png",
+ "bytes": 15866,
+ "sha256": "b4ca6dc4a67ae99cfa9d999c6a02ffebd54f77d2303056f5ef5552006900337a",
+ "width": 355,
+ "height": 369
+ },
+ {
+ "path": "slices/z/312.png",
+ "bytes": 15530,
+ "sha256": "b7fe23e5b140a008b02b6a5c2307c83836ade416b80022d053f7944da0b5363c",
+ "width": 356,
+ "height": 361
+ },
+ {
+ "path": "slices/z/313.png",
+ "bytes": 15307,
+ "sha256": "19e15bbb1b3108b79644cb1eb91f4d16b3f9b9d6e6ce6330a60ada8baa9ad76c",
+ "width": 353,
+ "height": 371
+ },
+ {
+ "path": "slices/z/314.png",
+ "bytes": 14635,
+ "sha256": "2756c97b38d3abfc169366b8812ccc8c683014bc08586bf157c9d25ef8e31736",
+ "width": 354,
+ "height": 367
+ },
+ {
+ "path": "slices/z/315.png",
+ "bytes": 14538,
+ "sha256": "2352e5c35300c2aa04775d489eecc9788194119578a4f43bff3f2c1d381f8b66",
+ "width": 352,
+ "height": 371
+ },
+ {
+ "path": "slices/z/316.png",
+ "bytes": 14327,
+ "sha256": "dac33eb8008ab9b855b12c056b8aae89473a9cab27ccbf99f9d87ed675c7b909",
+ "width": 356,
+ "height": 372
+ },
+ {
+ "path": "slices/z/317.png",
+ "bytes": 14031,
+ "sha256": "89cfac42786607117685160d6f467ab94a5ffdd1e22532f724b1c3bd002e9fe3",
+ "width": 357,
+ "height": 367
+ },
+ {
+ "path": "slices/z/318.png",
+ "bytes": 13533,
+ "sha256": "4eafb1344020cac0c96f0b35d594202b55c924da56304d7e18f7176f8ffdf2e7",
+ "width": 357,
+ "height": 363
+ },
+ {
+ "path": "slices/z/319.png",
+ "bytes": 13456,
+ "sha256": "c67d802a6efd67c975f3d00e92a14cd409fc9d62a3b6814919a2a51c9f159eb0",
+ "width": 356,
+ "height": 371
+ },
+ {
+ "path": "slices/z/32.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/320.png",
+ "bytes": 13060,
+ "sha256": "1a68e9441dbed665c6d4b8d3eb0bc8043dd7befaaf42153e45b32d19b4c06306",
+ "width": 351,
+ "height": 369
+ },
+ {
+ "path": "slices/z/321.png",
+ "bytes": 12599,
+ "sha256": "859568c9f32be4a41f09964f3d298bbf60dec57f19eaa54cad2ea8dbda930365",
+ "width": 355,
+ "height": 365
+ },
+ {
+ "path": "slices/z/322.png",
+ "bytes": 12461,
+ "sha256": "4fd4ae6b1899b7fba773ac8a68e9944aed52a74ee235eba66ea92b0146d6f046",
+ "width": 352,
+ "height": 368
+ },
+ {
+ "path": "slices/z/323.png",
+ "bytes": 12159,
+ "sha256": "f46a0b57fe88670422dad305fea594185462bb93f808036bd20550e7188c84d5",
+ "width": 356,
+ "height": 369
+ },
+ {
+ "path": "slices/z/324.png",
+ "bytes": 11845,
+ "sha256": "29c0c69a1b07954b181da3399f649fc350c16ed748782034b0005a9f3a56d393",
+ "width": 356,
+ "height": 373
+ },
+ {
+ "path": "slices/z/325.png",
+ "bytes": 11820,
+ "sha256": "22c767993e8b76fa851e63cdd2dd087d70c5ee2f95dd9793f8aa6b83437b4eb5",
+ "width": 356,
+ "height": 364
+ },
+ {
+ "path": "slices/z/326.png",
+ "bytes": 11226,
+ "sha256": "21d730a277e7d280e1be482d55e0e5657d3add7ffe52705f2b8628d27be330b9",
+ "width": 353,
+ "height": 363
+ },
+ {
+ "path": "slices/z/327.png",
+ "bytes": 11281,
+ "sha256": "6891d54b3373911da217c7bfb86adb4be2c0004b9577e8e3a4a94870c2d93f4d",
+ "width": 355,
+ "height": 368
+ },
+ {
+ "path": "slices/z/328.png",
+ "bytes": 11053,
+ "sha256": "2af5ea5d9c9014ea4d2fc1e9d49df362b4b9c529d1bf3d38b6a1492146a61402",
+ "width": 354,
+ "height": 367
+ },
+ {
+ "path": "slices/z/329.png",
+ "bytes": 10779,
+ "sha256": "5fb19ffda837a968a7c51d6b097cc3246d33ef637b2d15fd3c788014cd7e510b",
+ "width": 356,
+ "height": 361
+ },
+ {
+ "path": "slices/z/33.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/330.png",
+ "bytes": 10540,
+ "sha256": "3acaf90a00d4210ed0348ba7b190d22cf92b6a9405fb6d1960afe1939a634fea",
+ "width": 352,
+ "height": 366
+ },
+ {
+ "path": "slices/z/331.png",
+ "bytes": 10266,
+ "sha256": "a36b2fb153e35c4d0e9e41a8ed210bff853c187e85139aefd6a6d70c9a432860",
+ "width": 354,
+ "height": 360
+ },
+ {
+ "path": "slices/z/332.png",
+ "bytes": 10027,
+ "sha256": "9146687c64a7082c0d1c5d3337888e05512671d8f466cd6e07d3b1e7b51d2e0c",
+ "width": 356,
+ "height": 362
+ },
+ {
+ "path": "slices/z/333.png",
+ "bytes": 9594,
+ "sha256": "1d3cb85ec8e0c3664039d1cbde4a854331daf6647e108645f92da8846f1ba155",
+ "width": 350,
+ "height": 357
+ },
+ {
+ "path": "slices/z/334.png",
+ "bytes": 9339,
+ "sha256": "6f72f104f8c0a0791a4c62f179acda94dcada15a4e558f77c58517509881dd61",
+ "width": 353,
+ "height": 359
+ },
+ {
+ "path": "slices/z/335.png",
+ "bytes": 9280,
+ "sha256": "40444d97da426955abcb70faf6a4b64201d9b13bafb18d5156f8cfceb66e6b04",
+ "width": 353,
+ "height": 362
+ },
+ {
+ "path": "slices/z/336.png",
+ "bytes": 8758,
+ "sha256": "8015acb25fc4a64e3e1ae8eebff78a8c0fe5432810c1a65333ed693df23594cf",
+ "width": 351,
+ "height": 357
+ },
+ {
+ "path": "slices/z/337.png",
+ "bytes": 8498,
+ "sha256": "11ec7bf6cdf73dd060fe715278de40b20ed2141dec9d52a7b91dd4280a5b0eaf",
+ "width": 355,
+ "height": 358
+ },
+ {
+ "path": "slices/z/338.png",
+ "bytes": 8454,
+ "sha256": "c896262cb0800d40a7b381faacb7175f45f44dffb82cdd5b2e60ce31df7d17d2",
+ "width": 354,
+ "height": 359
+ },
+ {
+ "path": "slices/z/339.png",
+ "bytes": 8205,
+ "sha256": "04fb4f36ecfb1efa6cf2dd7a7c8262ebc0566b1be6b8f630987992989de8a59b",
+ "width": 356,
+ "height": 357
+ },
+ {
+ "path": "slices/z/34.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/340.png",
+ "bytes": 7902,
+ "sha256": "f916182e7e791864f63a84d8d3979d4fe3849c96eb5feb935f2059a82a562df7",
+ "width": 351,
+ "height": 363
+ },
+ {
+ "path": "slices/z/341.png",
+ "bytes": 7589,
+ "sha256": "0b1d6a7d3324ed2ba3a301d964eafeb11b58ab392d2aea8d128a7931f6aab6c5",
+ "width": 354,
+ "height": 358
+ },
+ {
+ "path": "slices/z/342.png",
+ "bytes": 7408,
+ "sha256": "89bd3570179bdec1c7347115f89d304a5af2aae32b7e98de5f050d975a125b98",
+ "width": 355,
+ "height": 360
+ },
+ {
+ "path": "slices/z/343.png",
+ "bytes": 6893,
+ "sha256": "f604d546244c7b78eb247864e9defb583f22e427173b5fdb3304bbb5fbdf16e2",
+ "width": 355,
+ "height": 358
+ },
+ {
+ "path": "slices/z/344.png",
+ "bytes": 6720,
+ "sha256": "9bf5dc50e89b6af28df88c0150bf64fb6131ba83223878ec79240e91f0730e63",
+ "width": 352,
+ "height": 358
+ },
+ {
+ "path": "slices/z/345.png",
+ "bytes": 6475,
+ "sha256": "c62e360736044461e630cf008af6bd8aa0b1cd6245c6ed501641c971e03049c4",
+ "width": 353,
+ "height": 359
+ },
+ {
+ "path": "slices/z/346.png",
+ "bytes": 6124,
+ "sha256": "ac49f337b1e043debfcc61d4e7f699f75f7ceeb76f956caeb2d522178aa95f84",
+ "width": 354,
+ "height": 359
+ },
+ {
+ "path": "slices/z/347.png",
+ "bytes": 6121,
+ "sha256": "b03732db4fc76c797cac2afc1f1ea6c4d05f0000dc855874b9a58c289d2fd5d1",
+ "width": 353,
+ "height": 353
+ },
+ {
+ "path": "slices/z/348.png",
+ "bytes": 6106,
+ "sha256": "357a100cb756b6bbfb832033e7d77e2d93c6bb2025c4045bd750c096b4ec941e",
+ "width": 354,
+ "height": 358
+ },
+ {
+ "path": "slices/z/349.png",
+ "bytes": 5646,
+ "sha256": "aa0d3f6b23e8fda03c29c20289d9e5c9bff9f248bfc28736b5a8a76a9fff824d",
+ "width": 355,
+ "height": 357
+ },
+ {
+ "path": "slices/z/35.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/350.png",
+ "bytes": 5521,
+ "sha256": "d65650d982c538244ad50b9f158ac6470f4b88954319a23f00381a6dd20845c5",
+ "width": 349,
+ "height": 355
+ },
+ {
+ "path": "slices/z/351.png",
+ "bytes": 5376,
+ "sha256": "acff46cf07ac1d777d396917eeb8222a63c878d33926cf904808b7f0c34aadeb",
+ "width": 355,
+ "height": 357
+ },
+ {
+ "path": "slices/z/352.png",
+ "bytes": 5080,
+ "sha256": "2bd854df48f8a0ec3dff9f907c8c92844f7ab7b16c6e2ca3e888ee8c9eafe1b9",
+ "width": 353,
+ "height": 355
+ },
+ {
+ "path": "slices/z/353.png",
+ "bytes": 4989,
+ "sha256": "bde8705861ad6642c708485935e49b534780878ed50653cdc2ffe6c9181ecf25",
+ "width": 355,
+ "height": 355
+ },
+ {
+ "path": "slices/z/354.png",
+ "bytes": 4939,
+ "sha256": "2ec93325d95b59adbdc20177950a5c989c80e2a4d22ce88d4e86a29ff0c8aebb",
+ "width": 352,
+ "height": 356
+ },
+ {
+ "path": "slices/z/355.png",
+ "bytes": 4830,
+ "sha256": "da1b04027c82def26fd1db1ddd9bfcd617eb0f887de0f0d306d3c2ab9cd95b84",
+ "width": 352,
+ "height": 359
+ },
+ {
+ "path": "slices/z/356.png",
+ "bytes": 4895,
+ "sha256": "38baa03b833fbc0c90b128fcb7d69d0a696a17761ed1bb36d56f3e4994a25a9f",
+ "width": 351,
+ "height": 355
+ },
+ {
+ "path": "slices/z/357.png",
+ "bytes": 4919,
+ "sha256": "f76ae4bdfb8f8afd8caa63cc17e194ce6f6d6fbe8c811da7b51efe28eabc0786",
+ "width": 355,
+ "height": 357
+ },
+ {
+ "path": "slices/z/358.png",
+ "bytes": 4618,
+ "sha256": "692a55fdfcbae0581e4112f3624e3a13b6ad2d8c4612dca1490adf57cb54f170",
+ "width": 353,
+ "height": 357
+ },
+ {
+ "path": "slices/z/359.png",
+ "bytes": 4385,
+ "sha256": "eb19f418a04fdfe00482b89311eb3fc4424a574b6d2ba9d411d24ca4eecf9b17",
+ "width": 355,
+ "height": 359
+ },
+ {
+ "path": "slices/z/36.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/360.png",
+ "bytes": 4356,
+ "sha256": "78dec019eca304b76585c1d9bc88f5c4a62099f860c592544bcd5ffcd5cda0e6",
+ "width": 352,
+ "height": 361
+ },
+ {
+ "path": "slices/z/361.png",
+ "bytes": 1009,
+ "sha256": "e437aa694faca8532d6cd2ac734f9cd2980e33c558a5f1ae0c2585432a4c49c3",
+ "width": 325,
+ "height": 352
+ },
+ {
+ "path": "slices/z/362.png",
+ "bytes": 877,
+ "sha256": "4e80f55c4aafd9d8f8c26625b41926b7fec710e7ba4cc1506630efe03d136c84",
+ "width": 308,
+ "height": 353
+ },
+ {
+ "path": "slices/z/363.png",
+ "bytes": 801,
+ "sha256": "f13025ac61b705bd963cdf74a93721b884597fd7c78fc701517f429be38ce7c0",
+ "width": 259,
+ "height": 349
+ },
+ {
+ "path": "slices/z/364.png",
+ "bytes": 778,
+ "sha256": "245110c0e6a3be5aba4604f3392ae409c5768049d93843ea8feb926d7bea30aa",
+ "width": 261,
+ "height": 352
+ },
+ {
+ "path": "slices/z/365.png",
+ "bytes": 869,
+ "sha256": "ac4334f3691f2862e7827732a7e35799ab88a6a3cf3c22ee6aaee3d1525b6cbc",
+ "width": 309,
+ "height": 353
+ },
+ {
+ "path": "slices/z/366.png",
+ "bytes": 862,
+ "sha256": "5747a1fe5925f8e70e5f36823173851f3a535afe075ef7c5634a01d89e41bf55",
+ "width": 342,
+ "height": 352
+ },
+ {
+ "path": "slices/z/367.png",
+ "bytes": 781,
+ "sha256": "a5cd3a98f60a4da7bde5e5597a41e545a152a3fd293ed0f2846cb563a0af2c24",
+ "width": 319,
+ "height": 329
+ },
+ {
+ "path": "slices/z/368.png",
+ "bytes": 739,
+ "sha256": "652cecc349fa976f3d4a0d1e9ebafe2c5661a8b3f0093ec53d22593b3a1f5e95",
+ "width": 318,
+ "height": 341
+ },
+ {
+ "path": "slices/z/369.png",
+ "bytes": 719,
+ "sha256": "257f6cd9b69516a62300cb8b253d4491176051c286846521347c0d96932d4ca1",
+ "width": 309,
+ "height": 342
+ },
+ {
+ "path": "slices/z/37.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/370.png",
+ "bytes": 619,
+ "sha256": "79f111a168622f7e614bd06b57287e5a2cda3362487920f461cc7f4c5dc79162",
+ "width": 263,
+ "height": 342
+ },
+ {
+ "path": "slices/z/371.png",
+ "bytes": 660,
+ "sha256": "6efda159a0fcc8fc534d2fcfbed5cfb85b89c16574a0cb02ab9eb3827aa756af",
+ "width": 306,
+ "height": 304
+ },
+ {
+ "path": "slices/z/372.png",
+ "bytes": 801,
+ "sha256": "0b3ba4eedad17a2302b2ec5db280a0c58eb0c62e1cd7869bab2560c15354ea1c",
+ "width": 303,
+ "height": 347
+ },
+ {
+ "path": "slices/z/373.png",
+ "bytes": 857,
+ "sha256": "e92d301661ad68b968a62c68af2c1112e4ce7a8a0c66485800662c96dc93a06a",
+ "width": 298,
+ "height": 359
+ },
+ {
+ "path": "slices/z/374.png",
+ "bytes": 854,
+ "sha256": "ff07c172f110ba9ed93d86cc9fab7207c3b7cefdec06145e5ffc604d56a271a7",
+ "width": 298,
+ "height": 359
+ },
+ {
+ "path": "slices/z/375.png",
+ "bytes": 684,
+ "sha256": "5436634cdd95b2c6d006773e92dca180e046ecff38512f60523f2a43109608a9",
+ "width": 270,
+ "height": 354
+ },
+ {
+ "path": "slices/z/376.png",
+ "bytes": 434,
+ "sha256": "30f96886674c3049dc958db8fb9d607b9ebab149f645164158cf1fe4aabaf153",
+ "width": 189,
+ "height": 285
+ },
+ {
+ "path": "slices/z/377.png",
+ "bytes": 621,
+ "sha256": "3b3044a24f30f5a835073d2e1f530d73e958d309d4b4bf87329b55fa8d238f82",
+ "width": 324,
+ "height": 305
+ },
+ {
+ "path": "slices/z/378.png",
+ "bytes": 637,
+ "sha256": "b38154e089e323b2c949a73f54ebce6bd4a61028584598ccd5b57c97fd5d70d0",
+ "width": 305,
+ "height": 352
+ },
+ {
+ "path": "slices/z/379.png",
+ "bytes": 635,
+ "sha256": "32ddc6664267ee3a027a377c5e0d35085c05e646e6002f5c62eb9a2c318b9d89",
+ "width": 325,
+ "height": 361
+ },
+ {
+ "path": "slices/z/38.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/380.png",
+ "bytes": 201,
+ "sha256": "2b205c6714705365febe5195fa4d921866eb50c7ec12b5a0a0c6d5de134d79fc",
+ "width": 122,
+ "height": 69
+ },
+ {
+ "path": "slices/z/381.png",
+ "bytes": 560,
+ "sha256": "b3d747fd8ff45c2dafa7bfb3f75023bd761db074e89e79e4c9cae559a493cb99",
+ "width": 331,
+ "height": 263
+ },
+ {
+ "path": "slices/z/382.png",
+ "bytes": 615,
+ "sha256": "c36f333247d689ff40edc28f1e3cf6ac4c360cd7d658e22ba833bf4b4a384bf5",
+ "width": 298,
+ "height": 325
+ },
+ {
+ "path": "slices/z/383.png",
+ "bytes": 237,
+ "sha256": "0c127d6a9b69db55920d017f42d4f6a07bec636cbfcaf355dc5b811d9d701cc4",
+ "width": 283,
+ "height": 71
+ },
+ {
+ "path": "slices/z/384.png",
+ "bytes": 585,
+ "sha256": "66bf89ca96029955479a61803243e81f91fdaeff9719e8b90207c417987c4cb3",
+ "width": 319,
+ "height": 330
+ },
+ {
+ "path": "slices/z/385.png",
+ "bytes": 534,
+ "sha256": "13f32c4116385ba41e736cd6376d092d82527212366b2c643a05b83ece8c19ec",
+ "width": 350,
+ "height": 250
+ },
+ {
+ "path": "slices/z/386.png",
+ "bytes": 555,
+ "sha256": "e63097ccaa4f28a7b653edf1da98b81978a64d7b60501f694d15e89131c59f71",
+ "width": 291,
+ "height": 334
+ },
+ {
+ "path": "slices/z/387.png",
+ "bytes": 473,
+ "sha256": "723ca3f871fe62fce01ef1b0f042f7e29f432e6fab627d90d26ec7eb3b1e5a74",
+ "width": 199,
+ "height": 347
+ },
+ {
+ "path": "slices/z/388.png",
+ "bytes": 351,
+ "sha256": "3641b1e2542cfa11957c3af52122641da38db4fe1e7a1f126f9373db2f5ed850",
+ "width": 201,
+ "height": 240
+ },
+ {
+ "path": "slices/z/389.png",
+ "bytes": 391,
+ "sha256": "15aa1366a480f43a7aae2523fdbbe293183cdae301df954f37579e0d7bca940d",
+ "width": 269,
+ "height": 220
+ },
+ {
+ "path": "slices/z/39.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/390.png",
+ "bytes": 565,
+ "sha256": "6fc26d41145d912f074c841a0dc53ab6d33af6af104c2e6ba31991c51d06a60b",
+ "width": 272,
+ "height": 325
+ },
+ {
+ "path": "slices/z/391.png",
+ "bytes": 524,
+ "sha256": "35e7600adf69c51afd56aa3b868128c54c42a0129cf36305d76f441d8964ad88",
+ "width": 252,
+ "height": 325
+ },
+ {
+ "path": "slices/z/392.png",
+ "bytes": 310,
+ "sha256": "2510d83b7bbb71ebd3326f4dca08b7a479307e809d6ad122b6d459088f4d0b82",
+ "width": 99,
+ "height": 303
+ },
+ {
+ "path": "slices/z/393.png",
+ "bytes": 583,
+ "sha256": "1aa97b936b676181a6178a8a2a09b2e8592538ca141c747c014d0572e1062222",
+ "width": 259,
+ "height": 328
+ },
+ {
+ "path": "slices/z/394.png",
+ "bytes": 599,
+ "sha256": "abd8ee6b683c9b3047ad799fd342f8c33bc18ed987e9bd9851e98a4670b3ee6f",
+ "width": 263,
+ "height": 328
+ },
+ {
+ "path": "slices/z/395.png",
+ "bytes": 597,
+ "sha256": "8e0cf513da60ec21834c275ca97594d1979b16616696d957915228a4b22df6f6",
+ "width": 341,
+ "height": 275
+ },
+ {
+ "path": "slices/z/396.png",
+ "bytes": 716,
+ "sha256": "2f91e5daabc26ea0b5bacd18c9691421dcc91cb271a73b1d2abcb049bb6a6997",
+ "width": 344,
+ "height": 337
+ },
+ {
+ "path": "slices/z/397.png",
+ "bytes": 606,
+ "sha256": "3528e55785d866f8d44e46f56b8d947844318b86288593417bac900625bd1ecf",
+ "width": 296,
+ "height": 327
+ },
+ {
+ "path": "slices/z/398.png",
+ "bytes": 712,
+ "sha256": "b490deef802e0ab5f6c66efba0808e98a9f1b202a419cf849a75ed6531f37059",
+ "width": 342,
+ "height": 313
+ },
+ {
+ "path": "slices/z/399.png",
+ "bytes": 727,
+ "sha256": "fd19c11daee30c69ad2591096e09f139abbcd0859d64b810e4da1c8eda06df2c",
+ "width": 349,
+ "height": 324
+ },
+ {
+ "path": "slices/z/40.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/400.png",
+ "bytes": 802,
+ "sha256": "ea4b83a46895a34dd78b1bc6dfaf32f2ba2902395be2901bbbfca1291b9dd7c8",
+ "width": 355,
+ "height": 333
+ },
+ {
+ "path": "slices/z/401.png",
+ "bytes": 821,
+ "sha256": "3a5c2f5c61fb2a4cfb3d5afba2cd2204e6a4f9b078ec9969760bb9f184f2c41a",
+ "width": 318,
+ "height": 331
+ },
+ {
+ "path": "slices/z/402.png",
+ "bytes": 745,
+ "sha256": "7c3164fae76b5baf5569e96e458944feb16b340e4d40e4f909bb36f308650cc2",
+ "width": 317,
+ "height": 267
+ },
+ {
+ "path": "slices/z/403.png",
+ "bytes": 434,
+ "sha256": "7a48c5921047d78a5251c525656f1a5d006636f02fb5d1b3498b3c9a97e53447",
+ "width": 71,
+ "height": 273
+ },
+ {
+ "path": "slices/z/404.png",
+ "bytes": 442,
+ "sha256": "fe7bb1bb0558bd75deee6ce4c04eea4c2fe74527fb02558747d6bf21a3e09bfc",
+ "width": 351,
+ "height": 72
+ },
+ {
+ "path": "slices/z/405.png",
+ "bytes": 422,
+ "sha256": "1c56b65e59ce064466a9bbb6d1ceb09bb5da634a81181cc7fe1112ad30d306c6",
+ "width": 350,
+ "height": 70
+ },
+ {
+ "path": "slices/z/406.png",
+ "bytes": 273,
+ "sha256": "305b484118216d6cc880f83d99b3568ebdeb3f15ca555f8461c29c58a7bab9c1",
+ "width": 57,
+ "height": 56
+ },
+ {
+ "path": "slices/z/407.png",
+ "bytes": 564,
+ "sha256": "72dd516c98e6fa637425cfaf4527bba1cdb0c57bd56c68714496645cedaae072",
+ "width": 220,
+ "height": 343
+ },
+ {
+ "path": "slices/z/408.png",
+ "bytes": 587,
+ "sha256": "343d218bbc5f829d894f9ee8123784c2bbd40c95b54fb7bfd1926566086e985f",
+ "width": 222,
+ "height": 344
+ },
+ {
+ "path": "slices/z/409.png",
+ "bytes": 768,
+ "sha256": "64baa41b0b669bc3a79ccc96193f5cf2db7472ffea5ef12471ffc570d293b830",
+ "width": 348,
+ "height": 339
+ },
+ {
+ "path": "slices/z/41.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/410.png",
+ "bytes": 729,
+ "sha256": "6d360bb9a3ae8eed0014df27e96f9d7e435dfc1dd99351b136fb0cc4fada5b44",
+ "width": 359,
+ "height": 293
+ },
+ {
+ "path": "slices/z/411.png",
+ "bytes": 452,
+ "sha256": "5fdae19b8b18dfc8cf57fc55c5dad887f092567e3d80b90eb8116fb752e46629",
+ "width": 166,
+ "height": 318
+ },
+ {
+ "path": "slices/z/412.png",
+ "bytes": 458,
+ "sha256": "ce7160d6a1751be89b53129e5deefee5cad42b9473478825f556a3d257357d63",
+ "width": 161,
+ "height": 318
+ },
+ {
+ "path": "slices/z/413.png",
+ "bytes": 638,
+ "sha256": "dac963ae37498e894c2efd79136e4c2e62b4c5e3141d959c6d8d441989b45f12",
+ "width": 302,
+ "height": 319
+ },
+ {
+ "path": "slices/z/414.png",
+ "bytes": 418,
+ "sha256": "ac47cabc7976d7c255705e87a8af5033d4efd2dfecdcd0d34c91479ac9a6f267",
+ "width": 307,
+ "height": 138
+ },
+ {
+ "path": "slices/z/415.png",
+ "bytes": 355,
+ "sha256": "8c5f44eee52a340ed7ac166e48976f6b980df9d35e92b3e2f36bfc84564f1f0a",
+ "width": 299,
+ "height": 129
+ },
+ {
+ "path": "slices/z/416.png",
+ "bytes": 340,
+ "sha256": "99334251add6cca0120103ba4cd5ce0e5d29ad5c44d0f932e9cbed8ee2e97ec3",
+ "width": 296,
+ "height": 128
+ },
+ {
+ "path": "slices/z/417.png",
+ "bytes": 239,
+ "sha256": "e2c0db02f616845e68da68f459dac028eb0a7de03f6fcbfcf387419a17ee8d85",
+ "width": 54,
+ "height": 258
+ },
+ {
+ "path": "slices/z/418.png",
+ "bytes": 216,
+ "sha256": "a3fa846ab4936b0e0629e2dc6af6ec33dbc7ec56ccf88c3d5d74d3c4c0a66558",
+ "width": 61,
+ "height": 255
+ },
+ {
+ "path": "slices/z/419.png",
+ "bytes": 176,
+ "sha256": "5f6553846730873af39d32add8a0570e138b3b7075eb83a8e6bd42ade039e35f",
+ "width": 40,
+ "height": 258
+ },
+ {
+ "path": "slices/z/42.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/420.png",
+ "bytes": 499,
+ "sha256": "1e9a277d1d4363c3df717b591e666c32d96662063fb93897b47cf9cdb42ddd3f",
+ "width": 305,
+ "height": 289
+ },
+ {
+ "path": "slices/z/421.png",
+ "bytes": 510,
+ "sha256": "6f97e4af0a4c7cfea5ac17199287e14a792b55f537b86fcaa41710dc0cfb75d1",
+ "width": 344,
+ "height": 275
+ },
+ {
+ "path": "slices/z/422.png",
+ "bytes": 164,
+ "sha256": "a4fd0616f49f70ed6347f99e2949944bf063d4c4fe9b5c7eedc679fad3c884aa",
+ "width": 345,
+ "height": 19
+ },
+ {
+ "path": "slices/z/423.png",
+ "bytes": 169,
+ "sha256": "cbdeef4d8a8ef13730f9b6527a7ff61ad686c6fd04561afbe73ba2abfc8e1e21",
+ "width": 341,
+ "height": 37
+ },
+ {
+ "path": "slices/z/424.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/425.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/426.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/427.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/428.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/429.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/43.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/430.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/431.png",
+ "bytes": 205,
+ "sha256": "0c19134612efad0243730e05fa54aa399a8e1284cbf5bff1e3caba308a81bf63",
+ "width": 86,
+ "height": 267
+ },
+ {
+ "path": "slices/z/432.png",
+ "bytes": 115,
+ "sha256": "f50f2d57af3c1825f87db3d2331145e6f6e3baf7dda396a475f80e4711ff85ce",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/433.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/434.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/435.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/436.png",
+ "bytes": 177,
+ "sha256": "e03545c8b4eb9f7d1c73437e56e54848f9c5b410a3b1e41ae507f2bfe5258e1a",
+ "width": 34,
+ "height": 297
+ },
+ {
+ "path": "slices/z/437.png",
+ "bytes": 173,
+ "sha256": "6321be2d926bf099d3048820625f1a91e8a61d30c54f25c6b53710b5d7f1afe2",
+ "width": 34,
+ "height": 297
+ },
+ {
+ "path": "slices/z/438.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/439.png",
+ "bytes": 151,
+ "sha256": "827217482addbe932449162ce8aabbe428e7b00566243a000fe31a2c3e95132d",
+ "width": 12,
+ "height": 149
+ },
+ {
+ "path": "slices/z/44.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/440.png",
+ "bytes": 303,
+ "sha256": "aebd76f129adbb7f8a71b81bb3a2cfa9fc101647a5dc718eed8358248e67f9fa",
+ "width": 221,
+ "height": 149
+ },
+ {
+ "path": "slices/z/441.png",
+ "bytes": 105,
+ "sha256": "3173867bf7b96080a2e0fd66169cabbb6f6547b543f7f8e584cccb5e2594b0cb",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/442.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/443.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/444.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/445.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/446.png",
+ "bytes": 102,
+ "sha256": "8e9220445da173706f4fc17a2bfdb0ee6e626fc61e42f1ce63c89916e4fbf442",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/447.png",
+ "bytes": 163,
+ "sha256": "173e305c29a0e92115a110b1bc02b0e9299cfc1e3a2216009959b2b5f153dfad",
+ "width": 8,
+ "height": 104
+ },
+ {
+ "path": "slices/z/448.png",
+ "bytes": 159,
+ "sha256": "28fc5c8a7585162f0adca92d7f11473c3235efa5fedfd10e32d84c3de379e907",
+ "width": 9,
+ "height": 103
+ },
+ {
+ "path": "slices/z/449.png",
+ "bytes": 98,
+ "sha256": "e801f04c3298f3f05553ad3b1430583e3eb43dcb8469791b0e3289c7d6a337b1",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/45.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/450.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/451.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/452.png",
+ "bytes": 112,
+ "sha256": "45230de7933083ed4b5ebd9ca32e0afc85888f324f4725a96645782f65642392",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/453.png",
+ "bytes": 111,
+ "sha256": "3e3143587425a2a380d3fbea5449d8b28f485e38d27e24d4c1fc0dec8b35ab8a",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/454.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/455.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/456.png",
+ "bytes": 114,
+ "sha256": "c8c0bed928d0d391806a39d40030121351fa168bf16d32b0e05667bb9a1cbd6d",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/457.png",
+ "bytes": 115,
+ "sha256": "95cd798117f3b348b42f5163c662e9139d33f580c8b3bd33376f0d263ed9459f",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/458.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/459.png",
+ "bytes": 115,
+ "sha256": "d9f1e38a738f52d98620fe7959b90aecbc7f79ee9dd067331b00e60259c068af",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/46.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/460.png",
+ "bytes": 100,
+ "sha256": "33596d96b5721dfb9d589fa496816dfaf073180da3c8663358a472d9de088f16",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/461.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/462.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/463.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/464.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/465.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/466.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/467.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/468.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/469.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/47.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/470.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/471.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/472.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/473.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/474.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/475.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/476.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/477.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/478.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/479.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/48.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/480.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/481.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/482.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/483.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/484.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/485.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/486.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/487.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/488.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/489.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/49.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/490.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/491.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/492.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/493.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/494.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/495.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/496.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/497.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/498.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/499.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/50.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/500.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/501.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/502.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/503.png",
+ "bytes": 139,
+ "sha256": "ea71eb64c182c6329b90a02b4519692d14f648810cbefcaa65d5a6a4f8274e19",
+ "width": 4,
+ "height": 64
+ },
+ {
+ "path": "slices/z/504.png",
+ "bytes": 162,
+ "sha256": "d310175759e9f67d3c8fcf0eb7e1347f3829777ea983bb090a5d5a1476c4bd0a",
+ "width": 5,
+ "height": 66
+ },
+ {
+ "path": "slices/z/505.png",
+ "bytes": 113,
+ "sha256": "441880487190520a0bd5a63cf1108a3d7a16f0e099c603e69de1cb1b8b6104ca",
+ "width": 3,
+ "height": 65
+ },
+ {
+ "path": "slices/z/506.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/507.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/508.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/509.png",
+ "bytes": 96,
+ "sha256": "19a19897b7e175583ec6dcbec149ec7ceeb17ba5cb5767c6f091f3b551848f7e",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/51.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/510.png",
+ "bytes": 103,
+ "sha256": "c1c07a41a13a31129d8b0665b71273e8dd4cf47cf88532ea56cc219f27794880",
+ "width": 3,
+ "height": 2
+ },
+ {
+ "path": "slices/z/511.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/52.png",
+ "bytes": 101,
+ "sha256": "f01deff89203818da5d4f56c65f6bd0a1d6f191a907a98dcee7a109f6029055d",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/53.png",
+ "bytes": 115,
+ "sha256": "f438cd2effa5a6992c265ef9bfda20ba817c3a9dcfe1c9de87a61c21a9a642d3",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/54.png",
+ "bytes": 101,
+ "sha256": "ca4606ef4c2569bf678950bfe6af53c2cc3cbc6b4cc7b434dcd22992ac34384a",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/55.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/56.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/57.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/58.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/59.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/60.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/61.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/62.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/63.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/64.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/65.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/66.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/67.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/68.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/69.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/70.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/71.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/72.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/73.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/74.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/75.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/76.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/77.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/78.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/79.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/80.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/81.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/82.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/83.png",
+ "bytes": 97,
+ "sha256": "2728f1721236afd351fdae5b478cf1bab3554070c56efa8f465251bef06eb56a",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/84.png",
+ "bytes": 169,
+ "sha256": "4c11a71143675862369a77aaa735d0d00dca7b3a3fd45ed6562fae7c026c4a5d",
+ "width": 5,
+ "height": 191
+ },
+ {
+ "path": "slices/z/85.png",
+ "bytes": 274,
+ "sha256": "fcc2c9f45bb2f4cf5c27f8a2aee7b5b876bbf5124970470904c441757f926eee",
+ "width": 89,
+ "height": 235
+ },
+ {
+ "path": "slices/z/86.png",
+ "bytes": 213,
+ "sha256": "992a9178d41b3ccf02a96890dec5152bd961504e24bb1ff47e700a66b9a66d99",
+ "width": 87,
+ "height": 233
+ },
+ {
+ "path": "slices/z/87.png",
+ "bytes": 93,
+ "sha256": "4efaabedc32b5564a7e8041df3cd0b50e0be86676a39f6629926ffd3f703fca9",
+ "width": 2,
+ "height": 1
+ },
+ {
+ "path": "slices/z/88.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/89.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/90.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/91.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/92.png",
+ "bytes": 101,
+ "sha256": "f01deff89203818da5d4f56c65f6bd0a1d6f191a907a98dcee7a109f6029055d",
+ "width": 2,
+ "height": 2
+ },
+ {
+ "path": "slices/z/93.png",
+ "bytes": 115,
+ "sha256": "5885a1303cadee146f12e6cdf9b3459ee4ee1da6a5fe6e1a56d00c0e2d39bf1a",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/94.png",
+ "bytes": 91,
+ "sha256": "defd343952dae18492be8e941378a82925f1f4e67b94c98e5980f39d4a759f3a",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/95.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/96.png",
+ "bytes": 106,
+ "sha256": "857f80ecce90bcb40c6e9f8df4d32c21c41fe4588c1548b7a3eb3d21358fa102",
+ "width": 3,
+ "height": 2
+ },
+ {
+ "path": "slices/z/97.png",
+ "bytes": 114,
+ "sha256": "8d5bea921fad53b8b64d8e9ab863eb809a30f435b6454cdce008b22741cf2f4d",
+ "width": 3,
+ "height": 3
+ },
+ {
+ "path": "slices/z/98.png",
+ "bytes": 91,
+ "sha256": "2fe8e6c2a228cc8932b1a02ef77d563dddc0618eb038ea3ea29338b35f37fdc6",
+ "width": 1,
+ "height": 1
+ },
+ {
+ "path": "slices/z/99.png",
+ "bytes": 104,
+ "sha256": "f0479c17280bc1eab93dbc0869bba7d1e2768c6b50d3426980c08c71b002950a",
+ "width": 2,
+ "height": 2
+ }
+ ],
+ "reusedHandoff": "round2-neutral/handoffs.json",
+ "verdict": "The unchanged neutral bytes and geometry retain the measured failed handoff verdict. This comparison is not visual acceptance."
+}
\ No newline at end of file
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/captures.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/captures.json
new file mode 100644
index 0000000000..135bb37708
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/captures.json
@@ -0,0 +1,96 @@
+{
+ "capturedAt": "2026-09-20T20:13:54.133Z",
+ "browser": "148.0.7778.96",
+ "platform": "darwin",
+ "viewport": {
+ "width": 1100,
+ "height": 820
+ },
+ "deviceScaleFactor": 1,
+ "url": "http://127.0.0.1:4339/@fs/private/tmp/cssearth-pr390/output/pr390-audit/layer-optimization/omega-preview.html",
+ "resultId": "7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549",
+ "logicalLayers": 459,
+ "domLeaves": 1377,
+ "stars": 0,
+ "source": "ESO/INAF-VST/OmegaCAM eso1119b photographic color on modeled integrated-starlight geometry",
+ "meaning": "Actual retained-DOM compiler viewer in a local inspection wrapper. This is not the main application or a promoted Lab delivery. Captures illustrate four camera poses, not matched before/after comparisons. No Pixelmatch comparison is claimed.",
+ "sideYLimit": "The interactive pitch clamp is 89 degrees, so this is a near-side view.",
+ "ignoredInputPins": [
+ {
+ "path": "output/pr390-audit/layer-optimization/omega-preview.html",
+ "bytes": 1907,
+ "sha256": "867d1a6e11ceffc7f3dcb0f75e68ac92913fd03e839b8be71c05c49f134c9432"
+ },
+ {
+ "path": "output/pr390-audit/layer-optimization/omega-preview.ts",
+ "bytes": 3699,
+ "sha256": "09ea277a99d21ae375a9ff9dacce952146e05a6eb8b6171eeead08efb6c9429d"
+ },
+ {
+ "path": "output/pr390-audit/layer-optimization/omega-7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549/scene.json",
+ "bytes": 17276,
+ "sha256": "250bccd18011bd0fafd5b5d17c756b393b17f886206ad1383b3fb47aab492fc8"
+ },
+ {
+ "path": ".local/nebula-lab/compiler/5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9/field.json",
+ "bytes": 4970176,
+ "sha256": "2f688583e71134410d5362c10068a7e51dc90db03b284592bdd28207f8a9bc94"
+ }
+ ],
+ "captures": [
+ {
+ "file": "front.png",
+ "bytes": 210440,
+ "sha256": "8aced76277035d66135f7b6afc130a8fc72613ed67b0ef1e712889b777684c6f",
+ "yawDegrees": 0,
+ "pitchDegrees": 0,
+ "framing": {
+ "zoom": 1,
+ "panX": 0,
+ "panY": 0
+ },
+ "lens": "eso1119b"
+ },
+ {
+ "file": "oblique.png",
+ "bytes": 213943,
+ "sha256": "4d57deb3c2b8baded9b54703fc69895842bf5b4f77cd31573d793fe2c03bbffd",
+ "yawDegrees": 35,
+ "pitchDegrees": 25,
+ "framing": {
+ "zoom": 1,
+ "panX": 0,
+ "panY": 0
+ },
+ "lens": "eso1119b"
+ },
+ {
+ "file": "side-x.png",
+ "bytes": 210233,
+ "sha256": "f31ffa91c300ef717eaeefc3fc208f953a4ca0ffdcf15efe3ea9be77149ebfef",
+ "yawDegrees": 90,
+ "pitchDegrees": 0,
+ "framing": {
+ "zoom": 1,
+ "panX": 0,
+ "panY": 0
+ },
+ "lens": "eso1119b"
+ },
+ {
+ "file": "side-y.png",
+ "bytes": 214418,
+ "sha256": "5e8b3a91fbd411056326a4ea8e6757ba1bdb2d2b5a6152d87734fca9b59997b8",
+ "yawDegrees": 0,
+ "pitchDegrees": 89,
+ "framing": {
+ "zoom": 1,
+ "panX": 0,
+ "panY": 0
+ },
+ "lens": "eso1119b"
+ }
+ ],
+ "errors": [],
+ "failures": []
+}
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/crab-comparison.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/crab-comparison.json
new file mode 100644
index 0000000000..552067690e
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/crab-comparison.json
@@ -0,0 +1,277 @@
+{
+ "id": "b3dba61dfa3cdc744220bde4abf59e3e7d52a4bc7f499d131edbac83f0effb5d",
+ "sourceResult": "7ffff9eabf29c89d06afe87187dfc3a9565044619b23d4d48aa47df01e9fb9dd",
+ "inputPin": {
+ "path": "src/objects/m1/source/compact/model.json.gz",
+ "sha256": "19a8d6d41d35694861e2ce5db6414f2d8f2a59ad9ea6d0129c3dc1d628b60be5"
+ },
+ "seconds": 494.772349208,
+ "plannedLayers": 391,
+ "referenceLayers": 547,
+ "proxy": {
+ "schema": "cssearth-layer-optimization@1",
+ "method": "gradient-weighted-depth-displacement@1",
+ "maximumLayers": 500,
+ "referenceLayers": 547,
+ "plannedLayers": 391,
+ "probeWidth": 64,
+ "probeSubpixels": 2,
+ "targetNormalizedL1": 0.01,
+ "estimatedNormalizedL1": {
+ "x": 0.00994595469364007,
+ "y": 0.009937762653267417,
+ "z": 0.00935854498311306
+ },
+ "status": "target-met"
+ },
+ "thresholds": {
+ "relativeLuminanceDifference": 0.05,
+ "normalizedL1": 0.04
+ },
+ "oldPassingHandoffs": 12,
+ "newPassingHandoffs": 12,
+ "newlyFailingHandoffs": [],
+ "rows": [
+ {
+ "id": "neutral",
+ "seam": "xz",
+ "oldL1": 0.01611342389813306,
+ "newL1": 0.021557419464845146,
+ "deltaL1": 0.005443995566712087,
+ "oldLuminance": 0.00027552104184480587,
+ "newLuminance": 0.00153794084585362,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "neutral",
+ "seam": "yz",
+ "oldL1": 0.014773440583234947,
+ "newL1": 0.02140600176955358,
+ "deltaL1": 0.0066325611863186345,
+ "oldLuminance": 0.00013169512030918756,
+ "newLuminance": 0.002474425224846249,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "hubble-optical",
+ "seam": "xz",
+ "oldL1": 0.019883459215999356,
+ "newL1": 0.025121290268514982,
+ "deltaL1": 0.005237831052515626,
+ "oldLuminance": 0.0012833102136353902,
+ "newLuminance": 0.0010019973280881017,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "hubble-optical",
+ "seam": "yz",
+ "oldL1": 0.018124596469084063,
+ "newL1": 0.024306401397345925,
+ "deltaL1": 0.006181804928261862,
+ "oldLuminance": 0.0007133230669537579,
+ "newLuminance": 0.0005227232072247526,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "webb-infrared",
+ "seam": "xz",
+ "oldL1": 0.021317259108527307,
+ "newL1": 0.025931641487936547,
+ "deltaL1": 0.00461438237940924,
+ "oldLuminance": 0.0018783274754755708,
+ "newLuminance": 0.0007795748246750415,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "webb-infrared",
+ "seam": "yz",
+ "oldL1": 0.019996894922608893,
+ "newL1": 0.025260506157460182,
+ "deltaL1": 0.005263611234851289,
+ "oldLuminance": 0.0004364466961828152,
+ "newLuminance": 0.0005879246850327958,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "webb-components",
+ "seam": "xz",
+ "oldL1": 0.024794299034011966,
+ "newL1": 0.028528778292168,
+ "deltaL1": 0.0037344792581560336,
+ "oldLuminance": 0.005105976928289425,
+ "newLuminance": 0.004902312949147532,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "webb-components",
+ "seam": "yz",
+ "oldL1": 0.024191560556441335,
+ "newL1": 0.02795225760892037,
+ "deltaL1": 0.0037606970524790354,
+ "oldLuminance": 0.00180017315430286,
+ "newLuminance": 0.0015339890776214166,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "spitzer-infrared",
+ "seam": "xz",
+ "oldL1": 0.04162007961576139,
+ "newL1": 0.04730136058002308,
+ "deltaL1": 0.005681280964261688,
+ "oldLuminance": 0.00817396217391176,
+ "newLuminance": 0.010624923970739386,
+ "oldPassed": false,
+ "newPassed": false
+ },
+ {
+ "id": "spitzer-infrared",
+ "seam": "yz",
+ "oldL1": 0.040298190851867435,
+ "newL1": 0.046997640170076405,
+ "deltaL1": 0.006699449318208969,
+ "oldLuminance": 0.000048080757939896155,
+ "newLuminance": 0.000039668396440669355,
+ "oldPassed": false,
+ "newPassed": false
+ },
+ {
+ "id": "vla-radio",
+ "seam": "xz",
+ "oldL1": 0.01966909289539202,
+ "newL1": 0.02430044708323659,
+ "deltaL1": 0.0046313541878445705,
+ "oldLuminance": 0.00016423927598018708,
+ "newLuminance": 0.0003125896665436954,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "vla-radio",
+ "seam": "yz",
+ "oldL1": 0.019484338848797633,
+ "newL1": 0.024533821109419485,
+ "deltaL1": 0.0050494822606218515,
+ "oldLuminance": 0.0012829696777013151,
+ "newLuminance": 0.0005796496642948497,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "chandra-xray",
+ "seam": "xz",
+ "oldL1": 0.014827151353326433,
+ "newL1": 0.014827151353326433,
+ "deltaL1": 0,
+ "oldLuminance": 0.0030534510270157854,
+ "newLuminance": 0.0030534510270157854,
+ "oldPassed": true,
+ "newPassed": true
+ },
+ {
+ "id": "chandra-xray",
+ "seam": "yz",
+ "oldL1": 0.013362636284053831,
+ "newL1": 0.013362636284053831,
+ "deltaL1": 0,
+ "oldLuminance": 0.0009057123734755019,
+ "newLuminance": 0.0009057123734755019,
+ "oldPassed": true,
+ "newPassed": true
+ }
+ ],
+ "payloads": [
+ {
+ "id": "neutral",
+ "oldLayers": 542,
+ "newLayers": 391,
+ "oldTextureBytes": 16608883,
+ "newTextureBytes": 15607732
+ },
+ {
+ "id": "hubble-optical",
+ "oldLayers": 542,
+ "newLayers": 391,
+ "oldTextureBytes": 96994439,
+ "newTextureBytes": 93807947
+ },
+ {
+ "id": "webb-infrared",
+ "oldLayers": 542,
+ "newLayers": 391,
+ "oldTextureBytes": 70359876,
+ "newTextureBytes": 68084516
+ },
+ {
+ "id": "webb-components",
+ "oldLayers": 542,
+ "newLayers": 391,
+ "oldTextureBytes": 82530133,
+ "newTextureBytes": 80394535
+ },
+ {
+ "id": "spitzer-infrared",
+ "oldLayers": 542,
+ "newLayers": 391,
+ "oldTextureBytes": 39738055,
+ "newTextureBytes": 38912299
+ },
+ {
+ "id": "vla-radio",
+ "oldLayers": 542,
+ "newLayers": 391,
+ "oldTextureBytes": 38441484,
+ "newTextureBytes": 37107868
+ },
+ {
+ "id": "chandra-xray",
+ "oldLayers": 542,
+ "newLayers": 391,
+ "oldTextureBytes": 1158012,
+ "newTextureBytes": 1088117
+ }
+ ],
+ "matchedViews": [
+ {
+ "lens": "neutral",
+ "pose": "front",
+ "oldVsNewNormalizedL1": 0.0042863111939171195,
+ "oldVsNewRelativeLuminanceDifference": 0.007934368685691588
+ },
+ {
+ "lens": "neutral",
+ "pose": "oblique",
+ "oldVsNewNormalizedL1": 0.0069732144048081125,
+ "oldVsNewRelativeLuminanceDifference": 0.009067779892565405
+ },
+ {
+ "lens": "hubble-optical",
+ "pose": "front",
+ "oldVsNewNormalizedL1": 0.00544403417960676,
+ "oldVsNewRelativeLuminanceDifference": 0.006831807070630873
+ },
+ {
+ "lens": "hubble-optical",
+ "pose": "oblique",
+ "oldVsNewNormalizedL1": 0.008467885979563996,
+ "oldVsNewRelativeLuminanceDifference": 0.0077562834323448415
+ }
+ ],
+ "actualBrowserErrors": {
+ "baseline": [],
+ "current": [],
+ "baselineFailedRequests": [],
+ "currentFailedRequests": [],
+ "baselineWrites": [],
+ "currentWrites": []
+ },
+ "retainedMounts": true,
+ "interpretation": "Real source-backed retained fields. Source pins/accepted package immutable. The planner proxy is not browser acceptance; every failed handoff remains a failure."
+}
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/front.png b/labs/nebula/models/omega-centauri/evidence/2026-09-20/front.png
new file mode 100644
index 0000000000..8c402ad1a9
Binary files /dev/null and b/labs/nebula/models/omega-centauri/evidence/2026-09-20/front.png differ
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-bake.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-bake.json
new file mode 100644
index 0000000000..10f73d50b8
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-bake.json
@@ -0,0 +1,1951 @@
+{
+ "id": "7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549",
+ "outputDirectory": "output/pr390-audit/layer-optimization/omega-7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549",
+ "elapsedSeconds": 230.938560959,
+ "sampling": {
+ "sliceCounts": {
+ "x": 147,
+ "y": 143,
+ "z": 171
+ },
+ "imageWidth": 512,
+ "samplesPerSlab": 4,
+ "layerPlan": {
+ "schema": "cssearth-volume-layer-plan@1",
+ "referenceSliceCounts": {
+ "x": 264,
+ "y": 264,
+ "z": 512
+ },
+ "referenceSamplesPerSlab": 4,
+ "axes": {
+ "x": [
+ {
+ "startCell": 0,
+ "endCell": 34
+ },
+ {
+ "startCell": 34,
+ "endCell": 41
+ },
+ {
+ "startCell": 41,
+ "endCell": 46
+ },
+ {
+ "startCell": 46,
+ "endCell": 49
+ },
+ {
+ "startCell": 49,
+ "endCell": 51
+ },
+ {
+ "startCell": 51,
+ "endCell": 54
+ },
+ {
+ "startCell": 54,
+ "endCell": 58
+ },
+ {
+ "startCell": 58,
+ "endCell": 61
+ },
+ {
+ "startCell": 61,
+ "endCell": 63
+ },
+ {
+ "startCell": 63,
+ "endCell": 66
+ },
+ {
+ "startCell": 66,
+ "endCell": 67
+ },
+ {
+ "startCell": 67,
+ "endCell": 68
+ },
+ {
+ "startCell": 68,
+ "endCell": 69
+ },
+ {
+ "startCell": 69,
+ "endCell": 71
+ },
+ {
+ "startCell": 71,
+ "endCell": 72
+ },
+ {
+ "startCell": 72,
+ "endCell": 73
+ },
+ {
+ "startCell": 73,
+ "endCell": 74
+ },
+ {
+ "startCell": 74,
+ "endCell": 77
+ },
+ {
+ "startCell": 77,
+ "endCell": 78
+ },
+ {
+ "startCell": 78,
+ "endCell": 79
+ },
+ {
+ "startCell": 79,
+ "endCell": 81
+ },
+ {
+ "startCell": 81,
+ "endCell": 82
+ },
+ {
+ "startCell": 82,
+ "endCell": 83
+ },
+ {
+ "startCell": 83,
+ "endCell": 84
+ },
+ {
+ "startCell": 84,
+ "endCell": 85
+ },
+ {
+ "startCell": 85,
+ "endCell": 86
+ },
+ {
+ "startCell": 86,
+ "endCell": 87
+ },
+ {
+ "startCell": 87,
+ "endCell": 88
+ },
+ {
+ "startCell": 88,
+ "endCell": 89
+ },
+ {
+ "startCell": 89,
+ "endCell": 90
+ },
+ {
+ "startCell": 90,
+ "endCell": 91
+ },
+ {
+ "startCell": 91,
+ "endCell": 92
+ },
+ {
+ "startCell": 92,
+ "endCell": 93
+ },
+ {
+ "startCell": 93,
+ "endCell": 94
+ },
+ {
+ "startCell": 94,
+ "endCell": 95
+ },
+ {
+ "startCell": 95,
+ "endCell": 96
+ },
+ {
+ "startCell": 96,
+ "endCell": 97
+ },
+ {
+ "startCell": 97,
+ "endCell": 98
+ },
+ {
+ "startCell": 98,
+ "endCell": 99
+ },
+ {
+ "startCell": 99,
+ "endCell": 100
+ },
+ {
+ "startCell": 100,
+ "endCell": 101
+ },
+ {
+ "startCell": 101,
+ "endCell": 102
+ },
+ {
+ "startCell": 102,
+ "endCell": 103
+ },
+ {
+ "startCell": 103,
+ "endCell": 104
+ },
+ {
+ "startCell": 104,
+ "endCell": 105
+ },
+ {
+ "startCell": 105,
+ "endCell": 106
+ },
+ {
+ "startCell": 106,
+ "endCell": 107
+ },
+ {
+ "startCell": 107,
+ "endCell": 108
+ },
+ {
+ "startCell": 108,
+ "endCell": 109
+ },
+ {
+ "startCell": 109,
+ "endCell": 110
+ },
+ {
+ "startCell": 110,
+ "endCell": 111
+ },
+ {
+ "startCell": 111,
+ "endCell": 112
+ },
+ {
+ "startCell": 112,
+ "endCell": 113
+ },
+ {
+ "startCell": 113,
+ "endCell": 114
+ },
+ {
+ "startCell": 114,
+ "endCell": 115
+ },
+ {
+ "startCell": 115,
+ "endCell": 116
+ },
+ {
+ "startCell": 116,
+ "endCell": 117
+ },
+ {
+ "startCell": 117,
+ "endCell": 118
+ },
+ {
+ "startCell": 118,
+ "endCell": 119
+ },
+ {
+ "startCell": 119,
+ "endCell": 120
+ },
+ {
+ "startCell": 120,
+ "endCell": 121
+ },
+ {
+ "startCell": 121,
+ "endCell": 122
+ },
+ {
+ "startCell": 122,
+ "endCell": 123
+ },
+ {
+ "startCell": 123,
+ "endCell": 124
+ },
+ {
+ "startCell": 124,
+ "endCell": 125
+ },
+ {
+ "startCell": 125,
+ "endCell": 126
+ },
+ {
+ "startCell": 126,
+ "endCell": 127
+ },
+ {
+ "startCell": 127,
+ "endCell": 128
+ },
+ {
+ "startCell": 128,
+ "endCell": 129
+ },
+ {
+ "startCell": 129,
+ "endCell": 130
+ },
+ {
+ "startCell": 130,
+ "endCell": 131
+ },
+ {
+ "startCell": 131,
+ "endCell": 132
+ },
+ {
+ "startCell": 132,
+ "endCell": 133
+ },
+ {
+ "startCell": 133,
+ "endCell": 134
+ },
+ {
+ "startCell": 134,
+ "endCell": 135
+ },
+ {
+ "startCell": 135,
+ "endCell": 136
+ },
+ {
+ "startCell": 136,
+ "endCell": 137
+ },
+ {
+ "startCell": 137,
+ "endCell": 138
+ },
+ {
+ "startCell": 138,
+ "endCell": 139
+ },
+ {
+ "startCell": 139,
+ "endCell": 140
+ },
+ {
+ "startCell": 140,
+ "endCell": 141
+ },
+ {
+ "startCell": 141,
+ "endCell": 142
+ },
+ {
+ "startCell": 142,
+ "endCell": 143
+ },
+ {
+ "startCell": 143,
+ "endCell": 144
+ },
+ {
+ "startCell": 144,
+ "endCell": 145
+ },
+ {
+ "startCell": 145,
+ "endCell": 146
+ },
+ {
+ "startCell": 146,
+ "endCell": 147
+ },
+ {
+ "startCell": 147,
+ "endCell": 148
+ },
+ {
+ "startCell": 148,
+ "endCell": 149
+ },
+ {
+ "startCell": 149,
+ "endCell": 150
+ },
+ {
+ "startCell": 150,
+ "endCell": 151
+ },
+ {
+ "startCell": 151,
+ "endCell": 152
+ },
+ {
+ "startCell": 152,
+ "endCell": 153
+ },
+ {
+ "startCell": 153,
+ "endCell": 154
+ },
+ {
+ "startCell": 154,
+ "endCell": 155
+ },
+ {
+ "startCell": 155,
+ "endCell": 156
+ },
+ {
+ "startCell": 156,
+ "endCell": 157
+ },
+ {
+ "startCell": 157,
+ "endCell": 158
+ },
+ {
+ "startCell": 158,
+ "endCell": 159
+ },
+ {
+ "startCell": 159,
+ "endCell": 160
+ },
+ {
+ "startCell": 160,
+ "endCell": 161
+ },
+ {
+ "startCell": 161,
+ "endCell": 162
+ },
+ {
+ "startCell": 162,
+ "endCell": 163
+ },
+ {
+ "startCell": 163,
+ "endCell": 164
+ },
+ {
+ "startCell": 164,
+ "endCell": 165
+ },
+ {
+ "startCell": 165,
+ "endCell": 166
+ },
+ {
+ "startCell": 166,
+ "endCell": 167
+ },
+ {
+ "startCell": 167,
+ "endCell": 168
+ },
+ {
+ "startCell": 168,
+ "endCell": 169
+ },
+ {
+ "startCell": 169,
+ "endCell": 170
+ },
+ {
+ "startCell": 170,
+ "endCell": 171
+ },
+ {
+ "startCell": 171,
+ "endCell": 172
+ },
+ {
+ "startCell": 172,
+ "endCell": 173
+ },
+ {
+ "startCell": 173,
+ "endCell": 174
+ },
+ {
+ "startCell": 174,
+ "endCell": 175
+ },
+ {
+ "startCell": 175,
+ "endCell": 176
+ },
+ {
+ "startCell": 176,
+ "endCell": 177
+ },
+ {
+ "startCell": 177,
+ "endCell": 178
+ },
+ {
+ "startCell": 178,
+ "endCell": 179
+ },
+ {
+ "startCell": 179,
+ "endCell": 180
+ },
+ {
+ "startCell": 180,
+ "endCell": 181
+ },
+ {
+ "startCell": 181,
+ "endCell": 182
+ },
+ {
+ "startCell": 182,
+ "endCell": 183
+ },
+ {
+ "startCell": 183,
+ "endCell": 184
+ },
+ {
+ "startCell": 184,
+ "endCell": 187
+ },
+ {
+ "startCell": 187,
+ "endCell": 189
+ },
+ {
+ "startCell": 189,
+ "endCell": 190
+ },
+ {
+ "startCell": 190,
+ "endCell": 191
+ },
+ {
+ "startCell": 191,
+ "endCell": 192
+ },
+ {
+ "startCell": 192,
+ "endCell": 195
+ },
+ {
+ "startCell": 195,
+ "endCell": 196
+ },
+ {
+ "startCell": 196,
+ "endCell": 197
+ },
+ {
+ "startCell": 197,
+ "endCell": 198
+ },
+ {
+ "startCell": 198,
+ "endCell": 199
+ },
+ {
+ "startCell": 199,
+ "endCell": 201
+ },
+ {
+ "startCell": 201,
+ "endCell": 204
+ },
+ {
+ "startCell": 204,
+ "endCell": 206
+ },
+ {
+ "startCell": 206,
+ "endCell": 207
+ },
+ {
+ "startCell": 207,
+ "endCell": 208
+ },
+ {
+ "startCell": 208,
+ "endCell": 210
+ },
+ {
+ "startCell": 210,
+ "endCell": 213
+ },
+ {
+ "startCell": 213,
+ "endCell": 216
+ },
+ {
+ "startCell": 216,
+ "endCell": 217
+ },
+ {
+ "startCell": 217,
+ "endCell": 219
+ },
+ {
+ "startCell": 219,
+ "endCell": 222
+ },
+ {
+ "startCell": 222,
+ "endCell": 225
+ },
+ {
+ "startCell": 225,
+ "endCell": 264
+ }
+ ],
+ "y": [
+ {
+ "startCell": 0,
+ "endCell": 39
+ },
+ {
+ "startCell": 39,
+ "endCell": 46
+ },
+ {
+ "startCell": 46,
+ "endCell": 48
+ },
+ {
+ "startCell": 48,
+ "endCell": 49
+ },
+ {
+ "startCell": 49,
+ "endCell": 51
+ },
+ {
+ "startCell": 51,
+ "endCell": 52
+ },
+ {
+ "startCell": 52,
+ "endCell": 53
+ },
+ {
+ "startCell": 53,
+ "endCell": 54
+ },
+ {
+ "startCell": 54,
+ "endCell": 55
+ },
+ {
+ "startCell": 55,
+ "endCell": 56
+ },
+ {
+ "startCell": 56,
+ "endCell": 57
+ },
+ {
+ "startCell": 57,
+ "endCell": 60
+ },
+ {
+ "startCell": 60,
+ "endCell": 63
+ },
+ {
+ "startCell": 63,
+ "endCell": 66
+ },
+ {
+ "startCell": 66,
+ "endCell": 69
+ },
+ {
+ "startCell": 69,
+ "endCell": 70
+ },
+ {
+ "startCell": 70,
+ "endCell": 73
+ },
+ {
+ "startCell": 73,
+ "endCell": 74
+ },
+ {
+ "startCell": 74,
+ "endCell": 75
+ },
+ {
+ "startCell": 75,
+ "endCell": 76
+ },
+ {
+ "startCell": 76,
+ "endCell": 77
+ },
+ {
+ "startCell": 77,
+ "endCell": 78
+ },
+ {
+ "startCell": 78,
+ "endCell": 79
+ },
+ {
+ "startCell": 79,
+ "endCell": 80
+ },
+ {
+ "startCell": 80,
+ "endCell": 81
+ },
+ {
+ "startCell": 81,
+ "endCell": 82
+ },
+ {
+ "startCell": 82,
+ "endCell": 83
+ },
+ {
+ "startCell": 83,
+ "endCell": 84
+ },
+ {
+ "startCell": 84,
+ "endCell": 87
+ },
+ {
+ "startCell": 87,
+ "endCell": 88
+ },
+ {
+ "startCell": 88,
+ "endCell": 89
+ },
+ {
+ "startCell": 89,
+ "endCell": 90
+ },
+ {
+ "startCell": 90,
+ "endCell": 91
+ },
+ {
+ "startCell": 91,
+ "endCell": 92
+ },
+ {
+ "startCell": 92,
+ "endCell": 93
+ },
+ {
+ "startCell": 93,
+ "endCell": 94
+ },
+ {
+ "startCell": 94,
+ "endCell": 95
+ },
+ {
+ "startCell": 95,
+ "endCell": 96
+ },
+ {
+ "startCell": 96,
+ "endCell": 97
+ },
+ {
+ "startCell": 97,
+ "endCell": 98
+ },
+ {
+ "startCell": 98,
+ "endCell": 99
+ },
+ {
+ "startCell": 99,
+ "endCell": 100
+ },
+ {
+ "startCell": 100,
+ "endCell": 101
+ },
+ {
+ "startCell": 101,
+ "endCell": 102
+ },
+ {
+ "startCell": 102,
+ "endCell": 103
+ },
+ {
+ "startCell": 103,
+ "endCell": 104
+ },
+ {
+ "startCell": 104,
+ "endCell": 105
+ },
+ {
+ "startCell": 105,
+ "endCell": 106
+ },
+ {
+ "startCell": 106,
+ "endCell": 107
+ },
+ {
+ "startCell": 107,
+ "endCell": 108
+ },
+ {
+ "startCell": 108,
+ "endCell": 109
+ },
+ {
+ "startCell": 109,
+ "endCell": 110
+ },
+ {
+ "startCell": 110,
+ "endCell": 111
+ },
+ {
+ "startCell": 111,
+ "endCell": 112
+ },
+ {
+ "startCell": 112,
+ "endCell": 113
+ },
+ {
+ "startCell": 113,
+ "endCell": 114
+ },
+ {
+ "startCell": 114,
+ "endCell": 115
+ },
+ {
+ "startCell": 115,
+ "endCell": 116
+ },
+ {
+ "startCell": 116,
+ "endCell": 117
+ },
+ {
+ "startCell": 117,
+ "endCell": 118
+ },
+ {
+ "startCell": 118,
+ "endCell": 119
+ },
+ {
+ "startCell": 119,
+ "endCell": 120
+ },
+ {
+ "startCell": 120,
+ "endCell": 121
+ },
+ {
+ "startCell": 121,
+ "endCell": 122
+ },
+ {
+ "startCell": 122,
+ "endCell": 123
+ },
+ {
+ "startCell": 123,
+ "endCell": 124
+ },
+ {
+ "startCell": 124,
+ "endCell": 125
+ },
+ {
+ "startCell": 125,
+ "endCell": 126
+ },
+ {
+ "startCell": 126,
+ "endCell": 127
+ },
+ {
+ "startCell": 127,
+ "endCell": 128
+ },
+ {
+ "startCell": 128,
+ "endCell": 129
+ },
+ {
+ "startCell": 129,
+ "endCell": 130
+ },
+ {
+ "startCell": 130,
+ "endCell": 131
+ },
+ {
+ "startCell": 131,
+ "endCell": 132
+ },
+ {
+ "startCell": 132,
+ "endCell": 133
+ },
+ {
+ "startCell": 133,
+ "endCell": 134
+ },
+ {
+ "startCell": 134,
+ "endCell": 135
+ },
+ {
+ "startCell": 135,
+ "endCell": 136
+ },
+ {
+ "startCell": 136,
+ "endCell": 137
+ },
+ {
+ "startCell": 137,
+ "endCell": 138
+ },
+ {
+ "startCell": 138,
+ "endCell": 139
+ },
+ {
+ "startCell": 139,
+ "endCell": 140
+ },
+ {
+ "startCell": 140,
+ "endCell": 141
+ },
+ {
+ "startCell": 141,
+ "endCell": 142
+ },
+ {
+ "startCell": 142,
+ "endCell": 143
+ },
+ {
+ "startCell": 143,
+ "endCell": 144
+ },
+ {
+ "startCell": 144,
+ "endCell": 145
+ },
+ {
+ "startCell": 145,
+ "endCell": 146
+ },
+ {
+ "startCell": 146,
+ "endCell": 147
+ },
+ {
+ "startCell": 147,
+ "endCell": 148
+ },
+ {
+ "startCell": 148,
+ "endCell": 149
+ },
+ {
+ "startCell": 149,
+ "endCell": 150
+ },
+ {
+ "startCell": 150,
+ "endCell": 151
+ },
+ {
+ "startCell": 151,
+ "endCell": 152
+ },
+ {
+ "startCell": 152,
+ "endCell": 153
+ },
+ {
+ "startCell": 153,
+ "endCell": 154
+ },
+ {
+ "startCell": 154,
+ "endCell": 155
+ },
+ {
+ "startCell": 155,
+ "endCell": 156
+ },
+ {
+ "startCell": 156,
+ "endCell": 157
+ },
+ {
+ "startCell": 157,
+ "endCell": 158
+ },
+ {
+ "startCell": 158,
+ "endCell": 159
+ },
+ {
+ "startCell": 159,
+ "endCell": 160
+ },
+ {
+ "startCell": 160,
+ "endCell": 161
+ },
+ {
+ "startCell": 161,
+ "endCell": 162
+ },
+ {
+ "startCell": 162,
+ "endCell": 163
+ },
+ {
+ "startCell": 163,
+ "endCell": 164
+ },
+ {
+ "startCell": 164,
+ "endCell": 165
+ },
+ {
+ "startCell": 165,
+ "endCell": 166
+ },
+ {
+ "startCell": 166,
+ "endCell": 167
+ },
+ {
+ "startCell": 167,
+ "endCell": 168
+ },
+ {
+ "startCell": 168,
+ "endCell": 169
+ },
+ {
+ "startCell": 169,
+ "endCell": 170
+ },
+ {
+ "startCell": 170,
+ "endCell": 171
+ },
+ {
+ "startCell": 171,
+ "endCell": 172
+ },
+ {
+ "startCell": 172,
+ "endCell": 173
+ },
+ {
+ "startCell": 173,
+ "endCell": 174
+ },
+ {
+ "startCell": 174,
+ "endCell": 175
+ },
+ {
+ "startCell": 175,
+ "endCell": 176
+ },
+ {
+ "startCell": 176,
+ "endCell": 177
+ },
+ {
+ "startCell": 177,
+ "endCell": 178
+ },
+ {
+ "startCell": 178,
+ "endCell": 179
+ },
+ {
+ "startCell": 179,
+ "endCell": 180
+ },
+ {
+ "startCell": 180,
+ "endCell": 182
+ },
+ {
+ "startCell": 182,
+ "endCell": 183
+ },
+ {
+ "startCell": 183,
+ "endCell": 184
+ },
+ {
+ "startCell": 184,
+ "endCell": 185
+ },
+ {
+ "startCell": 185,
+ "endCell": 187
+ },
+ {
+ "startCell": 187,
+ "endCell": 190
+ },
+ {
+ "startCell": 190,
+ "endCell": 191
+ },
+ {
+ "startCell": 191,
+ "endCell": 194
+ },
+ {
+ "startCell": 194,
+ "endCell": 197
+ },
+ {
+ "startCell": 197,
+ "endCell": 200
+ },
+ {
+ "startCell": 200,
+ "endCell": 203
+ },
+ {
+ "startCell": 203,
+ "endCell": 204
+ },
+ {
+ "startCell": 204,
+ "endCell": 207
+ },
+ {
+ "startCell": 207,
+ "endCell": 210
+ },
+ {
+ "startCell": 210,
+ "endCell": 213
+ },
+ {
+ "startCell": 213,
+ "endCell": 216
+ },
+ {
+ "startCell": 216,
+ "endCell": 219
+ },
+ {
+ "startCell": 219,
+ "endCell": 222
+ },
+ {
+ "startCell": 222,
+ "endCell": 225
+ },
+ {
+ "startCell": 225,
+ "endCell": 234
+ },
+ {
+ "startCell": 234,
+ "endCell": 264
+ }
+ ],
+ "z": [
+ {
+ "startCell": 0,
+ "endCell": 74
+ },
+ {
+ "startCell": 74,
+ "endCell": 95
+ },
+ {
+ "startCell": 95,
+ "endCell": 112
+ },
+ {
+ "startCell": 112,
+ "endCell": 117
+ },
+ {
+ "startCell": 117,
+ "endCell": 126
+ },
+ {
+ "startCell": 126,
+ "endCell": 134
+ },
+ {
+ "startCell": 134,
+ "endCell": 145
+ },
+ {
+ "startCell": 145,
+ "endCell": 156
+ },
+ {
+ "startCell": 156,
+ "endCell": 162
+ },
+ {
+ "startCell": 162,
+ "endCell": 169
+ },
+ {
+ "startCell": 169,
+ "endCell": 172
+ },
+ {
+ "startCell": 172,
+ "endCell": 177
+ },
+ {
+ "startCell": 177,
+ "endCell": 180
+ },
+ {
+ "startCell": 180,
+ "endCell": 181
+ },
+ {
+ "startCell": 181,
+ "endCell": 184
+ },
+ {
+ "startCell": 184,
+ "endCell": 187
+ },
+ {
+ "startCell": 187,
+ "endCell": 192
+ },
+ {
+ "startCell": 192,
+ "endCell": 195
+ },
+ {
+ "startCell": 195,
+ "endCell": 196
+ },
+ {
+ "startCell": 196,
+ "endCell": 197
+ },
+ {
+ "startCell": 197,
+ "endCell": 200
+ },
+ {
+ "startCell": 200,
+ "endCell": 203
+ },
+ {
+ "startCell": 203,
+ "endCell": 206
+ },
+ {
+ "startCell": 206,
+ "endCell": 209
+ },
+ {
+ "startCell": 209,
+ "endCell": 212
+ },
+ {
+ "startCell": 212,
+ "endCell": 213
+ },
+ {
+ "startCell": 213,
+ "endCell": 214
+ },
+ {
+ "startCell": 214,
+ "endCell": 216
+ },
+ {
+ "startCell": 216,
+ "endCell": 217
+ },
+ {
+ "startCell": 217,
+ "endCell": 218
+ },
+ {
+ "startCell": 218,
+ "endCell": 220
+ },
+ {
+ "startCell": 220,
+ "endCell": 221
+ },
+ {
+ "startCell": 221,
+ "endCell": 222
+ },
+ {
+ "startCell": 222,
+ "endCell": 223
+ },
+ {
+ "startCell": 223,
+ "endCell": 224
+ },
+ {
+ "startCell": 224,
+ "endCell": 225
+ },
+ {
+ "startCell": 225,
+ "endCell": 226
+ },
+ {
+ "startCell": 226,
+ "endCell": 227
+ },
+ {
+ "startCell": 227,
+ "endCell": 228
+ },
+ {
+ "startCell": 228,
+ "endCell": 229
+ },
+ {
+ "startCell": 229,
+ "endCell": 230
+ },
+ {
+ "startCell": 230,
+ "endCell": 231
+ },
+ {
+ "startCell": 231,
+ "endCell": 232
+ },
+ {
+ "startCell": 232,
+ "endCell": 233
+ },
+ {
+ "startCell": 233,
+ "endCell": 234
+ },
+ {
+ "startCell": 234,
+ "endCell": 235
+ },
+ {
+ "startCell": 235,
+ "endCell": 236
+ },
+ {
+ "startCell": 236,
+ "endCell": 237
+ },
+ {
+ "startCell": 237,
+ "endCell": 238
+ },
+ {
+ "startCell": 238,
+ "endCell": 239
+ },
+ {
+ "startCell": 239,
+ "endCell": 240
+ },
+ {
+ "startCell": 240,
+ "endCell": 241
+ },
+ {
+ "startCell": 241,
+ "endCell": 242
+ },
+ {
+ "startCell": 242,
+ "endCell": 243
+ },
+ {
+ "startCell": 243,
+ "endCell": 244
+ },
+ {
+ "startCell": 244,
+ "endCell": 245
+ },
+ {
+ "startCell": 245,
+ "endCell": 246
+ },
+ {
+ "startCell": 246,
+ "endCell": 247
+ },
+ {
+ "startCell": 247,
+ "endCell": 248
+ },
+ {
+ "startCell": 248,
+ "endCell": 249
+ },
+ {
+ "startCell": 249,
+ "endCell": 250
+ },
+ {
+ "startCell": 250,
+ "endCell": 251
+ },
+ {
+ "startCell": 251,
+ "endCell": 252
+ },
+ {
+ "startCell": 252,
+ "endCell": 253
+ },
+ {
+ "startCell": 253,
+ "endCell": 254
+ },
+ {
+ "startCell": 254,
+ "endCell": 255
+ },
+ {
+ "startCell": 255,
+ "endCell": 256
+ },
+ {
+ "startCell": 256,
+ "endCell": 257
+ },
+ {
+ "startCell": 257,
+ "endCell": 258
+ },
+ {
+ "startCell": 258,
+ "endCell": 259
+ },
+ {
+ "startCell": 259,
+ "endCell": 260
+ },
+ {
+ "startCell": 260,
+ "endCell": 261
+ },
+ {
+ "startCell": 261,
+ "endCell": 262
+ },
+ {
+ "startCell": 262,
+ "endCell": 263
+ },
+ {
+ "startCell": 263,
+ "endCell": 264
+ },
+ {
+ "startCell": 264,
+ "endCell": 265
+ },
+ {
+ "startCell": 265,
+ "endCell": 266
+ },
+ {
+ "startCell": 266,
+ "endCell": 267
+ },
+ {
+ "startCell": 267,
+ "endCell": 268
+ },
+ {
+ "startCell": 268,
+ "endCell": 269
+ },
+ {
+ "startCell": 269,
+ "endCell": 270
+ },
+ {
+ "startCell": 270,
+ "endCell": 271
+ },
+ {
+ "startCell": 271,
+ "endCell": 272
+ },
+ {
+ "startCell": 272,
+ "endCell": 273
+ },
+ {
+ "startCell": 273,
+ "endCell": 274
+ },
+ {
+ "startCell": 274,
+ "endCell": 275
+ },
+ {
+ "startCell": 275,
+ "endCell": 276
+ },
+ {
+ "startCell": 276,
+ "endCell": 277
+ },
+ {
+ "startCell": 277,
+ "endCell": 278
+ },
+ {
+ "startCell": 278,
+ "endCell": 279
+ },
+ {
+ "startCell": 279,
+ "endCell": 280
+ },
+ {
+ "startCell": 280,
+ "endCell": 281
+ },
+ {
+ "startCell": 281,
+ "endCell": 282
+ },
+ {
+ "startCell": 282,
+ "endCell": 283
+ },
+ {
+ "startCell": 283,
+ "endCell": 284
+ },
+ {
+ "startCell": 284,
+ "endCell": 285
+ },
+ {
+ "startCell": 285,
+ "endCell": 286
+ },
+ {
+ "startCell": 286,
+ "endCell": 287
+ },
+ {
+ "startCell": 287,
+ "endCell": 288
+ },
+ {
+ "startCell": 288,
+ "endCell": 289
+ },
+ {
+ "startCell": 289,
+ "endCell": 290
+ },
+ {
+ "startCell": 290,
+ "endCell": 291
+ },
+ {
+ "startCell": 291,
+ "endCell": 292
+ },
+ {
+ "startCell": 292,
+ "endCell": 293
+ },
+ {
+ "startCell": 293,
+ "endCell": 294
+ },
+ {
+ "startCell": 294,
+ "endCell": 295
+ },
+ {
+ "startCell": 295,
+ "endCell": 296
+ },
+ {
+ "startCell": 296,
+ "endCell": 297
+ },
+ {
+ "startCell": 297,
+ "endCell": 298
+ },
+ {
+ "startCell": 298,
+ "endCell": 299
+ },
+ {
+ "startCell": 299,
+ "endCell": 300
+ },
+ {
+ "startCell": 300,
+ "endCell": 301
+ },
+ {
+ "startCell": 301,
+ "endCell": 302
+ },
+ {
+ "startCell": 302,
+ "endCell": 303
+ },
+ {
+ "startCell": 303,
+ "endCell": 304
+ },
+ {
+ "startCell": 304,
+ "endCell": 305
+ },
+ {
+ "startCell": 305,
+ "endCell": 306
+ },
+ {
+ "startCell": 306,
+ "endCell": 307
+ },
+ {
+ "startCell": 307,
+ "endCell": 308
+ },
+ {
+ "startCell": 308,
+ "endCell": 309
+ },
+ {
+ "startCell": 309,
+ "endCell": 310
+ },
+ {
+ "startCell": 310,
+ "endCell": 311
+ },
+ {
+ "startCell": 311,
+ "endCell": 312
+ },
+ {
+ "startCell": 312,
+ "endCell": 313
+ },
+ {
+ "startCell": 313,
+ "endCell": 314
+ },
+ {
+ "startCell": 314,
+ "endCell": 315
+ },
+ {
+ "startCell": 315,
+ "endCell": 316
+ },
+ {
+ "startCell": 316,
+ "endCell": 317
+ },
+ {
+ "startCell": 317,
+ "endCell": 318
+ },
+ {
+ "startCell": 318,
+ "endCell": 319
+ },
+ {
+ "startCell": 319,
+ "endCell": 320
+ },
+ {
+ "startCell": 320,
+ "endCell": 321
+ },
+ {
+ "startCell": 321,
+ "endCell": 323
+ },
+ {
+ "startCell": 323,
+ "endCell": 324
+ },
+ {
+ "startCell": 324,
+ "endCell": 325
+ },
+ {
+ "startCell": 325,
+ "endCell": 326
+ },
+ {
+ "startCell": 326,
+ "endCell": 327
+ },
+ {
+ "startCell": 327,
+ "endCell": 330
+ },
+ {
+ "startCell": 330,
+ "endCell": 331
+ },
+ {
+ "startCell": 331,
+ "endCell": 334
+ },
+ {
+ "startCell": 334,
+ "endCell": 335
+ },
+ {
+ "startCell": 335,
+ "endCell": 338
+ },
+ {
+ "startCell": 338,
+ "endCell": 339
+ },
+ {
+ "startCell": 339,
+ "endCell": 340
+ },
+ {
+ "startCell": 340,
+ "endCell": 343
+ },
+ {
+ "startCell": 343,
+ "endCell": 345
+ },
+ {
+ "startCell": 345,
+ "endCell": 348
+ },
+ {
+ "startCell": 348,
+ "endCell": 351
+ },
+ {
+ "startCell": 351,
+ "endCell": 354
+ },
+ {
+ "startCell": 354,
+ "endCell": 357
+ },
+ {
+ "startCell": 357,
+ "endCell": 358
+ },
+ {
+ "startCell": 358,
+ "endCell": 360
+ },
+ {
+ "startCell": 360,
+ "endCell": 363
+ },
+ {
+ "startCell": 363,
+ "endCell": 366
+ },
+ {
+ "startCell": 366,
+ "endCell": 369
+ },
+ {
+ "startCell": 369,
+ "endCell": 373
+ },
+ {
+ "startCell": 373,
+ "endCell": 374
+ },
+ {
+ "startCell": 374,
+ "endCell": 375
+ },
+ {
+ "startCell": 375,
+ "endCell": 376
+ },
+ {
+ "startCell": 376,
+ "endCell": 380
+ },
+ {
+ "startCell": 380,
+ "endCell": 385
+ },
+ {
+ "startCell": 385,
+ "endCell": 390
+ },
+ {
+ "startCell": 390,
+ "endCell": 401
+ },
+ {
+ "startCell": 401,
+ "endCell": 409
+ },
+ {
+ "startCell": 409,
+ "endCell": 411
+ },
+ {
+ "startCell": 411,
+ "endCell": 420
+ },
+ {
+ "startCell": 420,
+ "endCell": 447
+ },
+ {
+ "startCell": 447,
+ "endCell": 450
+ },
+ {
+ "startCell": 450,
+ "endCell": 455
+ },
+ {
+ "startCell": 455,
+ "endCell": 458
+ },
+ {
+ "startCell": 458,
+ "endCell": 512
+ }
+ ]
+ }
+ },
+ "layerOptimization": {
+ "schema": "cssearth-layer-optimization@1",
+ "method": "gradient-weighted-depth-displacement@1",
+ "maximumLayers": 500,
+ "referenceLayers": 1040,
+ "plannedLayers": 461,
+ "probeWidth": 64,
+ "probeSubpixels": 2,
+ "targetNormalizedL1": 0.01,
+ "estimatedNormalizedL1": {
+ "x": 0.0098247993518533,
+ "y": 0.00981228328179792,
+ "z": 0.009852407570529206
+ },
+ "status": "target-met"
+ }
+ },
+ "banks": [
+ {
+ "id": "neutral",
+ "counts": [
+ {
+ "axis": "x",
+ "retained": 146
+ },
+ {
+ "axis": "y",
+ "retained": 142
+ },
+ {
+ "axis": "z",
+ "retained": 171
+ }
+ ],
+ "resources": 459,
+ "bytes": 5954296
+ },
+ {
+ "id": "eso1119b",
+ "counts": [
+ {
+ "axis": "x",
+ "retained": 146
+ },
+ {
+ "axis": "y",
+ "retained": 142
+ },
+ {
+ "axis": "z",
+ "retained": 171
+ }
+ ],
+ "resources": 459,
+ "bytes": 13866551
+ },
+ {
+ "id": "eso0844a",
+ "counts": [
+ {
+ "axis": "x",
+ "retained": 146
+ },
+ {
+ "axis": "y",
+ "retained": 142
+ },
+ {
+ "axis": "z",
+ "retained": 171
+ }
+ ],
+ "resources": 459,
+ "bytes": 13158283
+ }
+ ],
+ "status": "inspection-only-unaccepted"
+}
\ No newline at end of file
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-handoffs.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-handoffs.json
new file mode 100644
index 0000000000..4aac2528fd
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-handoffs.json
@@ -0,0 +1,176 @@
+{
+ "oldId": "5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9",
+ "optimizedId": "7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549",
+ "thresholds": {
+ "relativeLuminanceDifference": 0.05,
+ "normalizedL1": 0.04
+ },
+ "measurements": [
+ {
+ "version": "old",
+ "lens": "neutral",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.007448274204638095,
+ "normalizedL1": 0.10579373570430688,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "neutral",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.0013473004012343357,
+ "normalizedL1": 0.09407192435762682,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "neutral",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.5939720780803218,
+ "normalizedL1": 0.4445885734295744,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso1119b",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.007659996564966913,
+ "normalizedL1": 0.10335238699683513,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso1119b",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.0013421471217446319,
+ "normalizedL1": 0.09204724768477665,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso1119b",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.013268410687652613,
+ "normalizedL1": 0.10212193390601276,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso0844a",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.007485076722130978,
+ "normalizedL1": 0.10509154123884833,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso0844a",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.0013441126162484919,
+ "normalizedL1": 0.0933923526990095,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso0844a",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.013233523895923052,
+ "normalizedL1": 0.1035187536996954,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "neutral",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.004804672942986058,
+ "normalizedL1": 0.12498652413707272,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "neutral",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.003411535415403307,
+ "normalizedL1": 0.1146546907233652,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "neutral",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.05648728604634782,
+ "normalizedL1": 0.13380665807723602,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso1119b",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.005002309945527142,
+ "normalizedL1": 0.12007917840015177,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso1119b",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.0033750817084131296,
+ "normalizedL1": 0.11017354402412256,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso1119b",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.0128238570542583,
+ "normalizedL1": 0.1240519478966761,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso0844a",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.004756726423958046,
+ "normalizedL1": 0.1238774224552733,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso0844a",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.0034671649169563516,
+ "normalizedL1": 0.11369655642048106,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso0844a",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.012856416048208702,
+ "normalizedL1": 0.12853615472121113,
+ "passed": false
+ }
+ ],
+ "frontChanges": [
+ {
+ "lens": "neutral",
+ "relativeLuminanceDifference": 0.011858627486820459,
+ "normalizedL1": 0.006356681438366735,
+ "passed": true
+ },
+ {
+ "lens": "eso1119b",
+ "relativeLuminanceDifference": 0.011988540643949658,
+ "normalizedL1": 0.0064763183755192225,
+ "passed": true
+ },
+ {
+ "lens": "eso0844a",
+ "relativeLuminanceDifference": 0.012053502422455805,
+ "normalizedL1": 0.0064513515253360205,
+ "passed": true
+ }
+ ],
+ "errors": [],
+ "failed": []
+}
\ No newline at end of file
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/legacy-handoffs.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/legacy-handoffs.json
new file mode 100644
index 0000000000..6bc18a3dd5
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/legacy-handoffs.json
@@ -0,0 +1,38 @@
+{
+ "id": "412d070141ce5119bd773d7a1eec65f12f78fa36ced3f930aa4fc9491fa7bdca",
+ "errors": [],
+ "thresholds": {
+ "relativeLuminanceDifference": 0.05,
+ "normalizedL1": 0.04
+ },
+ "measurements": [
+ {
+ "pixelated": false,
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.007448274204638095,
+ "normalizedL1": 0.10579373570430688,
+ "passed": false
+ },
+ {
+ "pixelated": false,
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.0013473004012343357,
+ "normalizedL1": 0.09407192435762682,
+ "passed": false
+ },
+ {
+ "pixelated": true,
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.0008566774711162329,
+ "normalizedL1": 0.09625850191616052,
+ "passed": false
+ },
+ {
+ "pixelated": true,
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.00038089515411631034,
+ "normalizedL1": 0.08279774555926805,
+ "passed": false
+ }
+ ]
+}
\ No newline at end of file
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/numerical-alignment.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/numerical-alignment.json
new file mode 100644
index 0000000000..190c15b7f5
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/numerical-alignment.json
@@ -0,0 +1,971 @@
+{
+ "elapsedSeconds": 4.09,
+ "method": "Fixed full 800x700 canvas and all-pixel mask. RGB L1; unchanged source-total normalization before/after. Bilinear similarity, no gain fit. Bounded coordinate search at stride4; final RGB L1 evaluated over every pixel. Translation seeds: full +/-24px grid at4px intervals, stride8 objective, best3 seeds plus zero refined at stride4. Bounds +/-24px, scale .98..1.02, rotation +/-5deg; best sampled/local solution, not guaranteed global optimum.",
+ "syntheticControl": {
+ "appliedShift": [
+ 3,
+ -2
+ ],
+ "before": 0.18041813155996742,
+ "recovered": {
+ "parameters": [
+ -3,
+ 2,
+ 1,
+ 0
+ ],
+ "coarseL1": 0,
+ "normalizedL1": 0.000018555444647663342
+ }
+ },
+ "results": [
+ {
+ "version": "old",
+ "lens": "neutral",
+ "seam": "xz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 350.0064266846449,
+ 355.77001760745884
+ ],
+ "sigmaMajor": 95.03931091269595,
+ "sigmaMinor": 85.08985207205704,
+ "angleDegrees": -49.13066179906404,
+ "mass": 5286396
+ },
+ "b": {
+ "centroid": [
+ 365.96897015545807,
+ 355.46853287210484
+ ],
+ "sigmaMajor": 93.85082727018796,
+ "sigmaMinor": 84.03475232316816,
+ "angleDegrees": -49.96585334983239,
+ "mass": 5326066
+ },
+ "centroidDifference": [
+ 15.96254347081316,
+ -0.3014847353539949
+ ],
+ "majorScaleRatio": 0.9874948205001218,
+ "minorScaleRatio": 0.9876001694303642,
+ "angleDifferenceDegrees": -0.835191550768343,
+ "centroidCorrectionL1": 0.2360383458126251,
+ "before": 0.10579373570430688,
+ "translation": {
+ "parameters": [
+ -0.25,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.10039465863927553,
+ "normalizedL1": 0.10049603711184078
+ },
+ "similarity": {
+ "parameters": [
+ -0.25,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.10039465863927553,
+ "normalizedL1": 0.10049603711184078
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00100; not a statistical confidence interval",
+ "ranges": [
+ [
+ -0.25,
+ -0.25
+ ],
+ [
+ -0.25,
+ -0.25
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "old",
+ "lens": "neutral",
+ "seam": "yz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 397.6263259381392,
+ 401.8625538624582
+ ],
+ "sigmaMajor": 93.46189701109002,
+ "sigmaMinor": 89.45292106956612,
+ "angleDegrees": -6.470208775133606,
+ "mass": 5582274
+ },
+ "b": {
+ "centroid": [
+ 398.0670329250462,
+ 385.759743974307
+ ],
+ "sigmaMajor": 93.17063280546337,
+ "sigmaMinor": 88.00766392314706,
+ "angleDegrees": -2.3133329166680996,
+ "mass": 5574753
+ },
+ "centroidDifference": [
+ 0.44070698690700283,
+ -16.10280988815117
+ ],
+ "majorScaleRatio": 0.9968836048171364,
+ "minorScaleRatio": 0.9838433767266795,
+ "angleDifferenceDegrees": 4.156875858465526,
+ "centroidCorrectionL1": 0.2159553706922375,
+ "before": 0.09407192435762682,
+ "translation": {
+ "parameters": [
+ 0,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.0903434776196129,
+ "normalizedL1": 0.09131007749645134
+ },
+ "similarity": {
+ "parameters": [
+ 0,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.0903434776196129,
+ "normalizedL1": 0.09131007749645134
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00100; not a statistical confidence interval",
+ "ranges": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -0.25,
+ 0.25
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "old",
+ "lens": "eso1119b",
+ "seam": "xz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 349.6579603922409,
+ 355.9975415625327
+ ],
+ "sigmaMajor": 92.83735052601119,
+ "sigmaMinor": 82.95231080126369,
+ "angleDegrees": -47.18861973337692,
+ "mass": 5179924.617399923
+ },
+ "b": {
+ "centroid": [
+ 365.9382902285517,
+ 355.681207741813
+ ],
+ "sigmaMajor": 91.64621130634593,
+ "sigmaMinor": 81.86899224156288,
+ "angleDegrees": -47.8909024819492,
+ "mass": 5219909.103199873
+ },
+ "centroidDifference": [
+ 16.280329836310784,
+ -0.3163338207197057
+ ],
+ "majorScaleRatio": 0.9871696120912937,
+ "minorScaleRatio": 0.9869404655610352,
+ "angleDifferenceDegrees": -0.7022827485722587,
+ "centroidCorrectionL1": 0.22106274205576107,
+ "before": 0.10335238699683513,
+ "translation": {
+ "parameters": [
+ -0.25,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.09817370148743199,
+ "normalizedL1": 0.0982866508433419
+ },
+ "similarity": {
+ "parameters": [
+ -0.25,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.09817370148743199,
+ "normalizedL1": 0.0982866508433419
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00100; not a statistical confidence interval",
+ "ranges": [
+ [
+ -0.25,
+ -0.25
+ ],
+ [
+ -0.25,
+ -0.25
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "old",
+ "lens": "eso1119b",
+ "seam": "yz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 397.45965795753074,
+ 402.3557215217105
+ ],
+ "sigmaMajor": 91.34570467038505,
+ "sigmaMinor": 87.7182351448285,
+ "angleDegrees": -6.68938330459356,
+ "mass": 5477879.9439999135
+ },
+ "b": {
+ "centroid": [
+ 397.89556064471844,
+ 385.9369599603437
+ ],
+ "sigmaMajor": 91.03717999217143,
+ "sigmaMinor": 86.27358360005815,
+ "angleDegrees": -2.222860490476753,
+ "mass": 5470527.823199811
+ },
+ "centroidDifference": [
+ 0.4359026871877063,
+ -16.418761561366807
+ ],
+ "majorScaleRatio": 0.9966224500722075,
+ "minorScaleRatio": 0.9835307727933065,
+ "angleDifferenceDegrees": 4.466522814116786,
+ "centroidCorrectionL1": 0.20138040610872904,
+ "before": 0.09204724768477665,
+ "translation": {
+ "parameters": [
+ 0,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.08868853908693972,
+ "normalizedL1": 0.0896051809063681
+ },
+ "similarity": {
+ "parameters": [
+ 0,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.08868853908693972,
+ "normalizedL1": 0.0896051809063681
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00100; not a statistical confidence interval",
+ "ranges": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -0.25,
+ 0.25
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "old",
+ "lens": "eso0844a",
+ "seam": "xz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 349.61037451321914,
+ 355.54182945265217
+ ],
+ "sigmaMajor": 94.68134119726498,
+ "sigmaMinor": 84.61778784028618,
+ "angleDegrees": -49.43266501919921,
+ "mass": 5178017.786799854
+ },
+ "b": {
+ "centroid": [
+ 365.92153688301556,
+ 355.23212867373866
+ ],
+ "sigmaMajor": 93.4789893229741,
+ "sigmaMinor": 83.59166417112729,
+ "angleDegrees": -50.17179399392732,
+ "mass": 5217067.940599813
+ },
+ "centroidDifference": [
+ 16.31116236979642,
+ -0.3097007789135091
+ ],
+ "majorScaleRatio": 0.9873010684144637,
+ "minorScaleRatio": 0.9878734283257833,
+ "angleDifferenceDegrees": -0.7391289747280894,
+ "centroidCorrectionL1": 0.22806954301194038,
+ "before": 0.10509154123884833,
+ "translation": {
+ "parameters": [
+ -0.25,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.09977830850583706,
+ "normalizedL1": 0.09989175625476641
+ },
+ "similarity": {
+ "parameters": [
+ -0.25,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.09977830850583706,
+ "normalizedL1": 0.09989175625476641
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00100; not a statistical confidence interval",
+ "ranges": [
+ [
+ -0.25,
+ -0.25
+ ],
+ [
+ -0.25,
+ -0.25
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "old",
+ "lens": "eso0844a",
+ "seam": "yz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 397.30549349144434,
+ 401.8976439882904
+ ],
+ "sigmaMajor": 93.04669263646544,
+ "sigmaMinor": 88.92915431085648,
+ "angleDegrees": -7.063430785311585,
+ "mass": 5475537.176799839
+ },
+ "b": {
+ "centroid": [
+ 397.7515575402513,
+ 385.4760918111895
+ ],
+ "sigmaMajor": 92.74870129014046,
+ "sigmaMinor": 87.42422249163049,
+ "angleDegrees": -3.1271862542095747,
+ "mass": 5468177.438199765
+ },
+ "centroidDifference": [
+ 0.44606404880698847,
+ -16.421552177100864
+ ],
+ "majorScaleRatio": 0.996797399908783,
+ "minorScaleRatio": 0.9830771828329165,
+ "angleDifferenceDegrees": 3.9362445311020338,
+ "centroidCorrectionL1": 0.20801594722105282,
+ "before": 0.0933923526990095,
+ "translation": {
+ "parameters": [
+ 0,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.08987258031063923,
+ "normalizedL1": 0.09085101652838905
+ },
+ "similarity": {
+ "parameters": [
+ 0,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.08987258031063923,
+ "normalizedL1": 0.09085101652838905
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00100; not a statistical confidence interval",
+ "ranges": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -0.25,
+ 0.25
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "optimized",
+ "lens": "neutral",
+ "seam": "xz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 349.20568841566853,
+ 355.8665634667417
+ ],
+ "sigmaMajor": 95.86688695680532,
+ "sigmaMinor": 86.1219703785479,
+ "angleDegrees": -46.32806453745209,
+ "mass": 5362407
+ },
+ "b": {
+ "centroid": [
+ 366.3211416002387,
+ 355.84109150648
+ ],
+ "sigmaMajor": 94.89264066554335,
+ "sigmaMinor": 85.31350767773863,
+ "angleDegrees": -47.566546227363844,
+ "mass": 5388296
+ },
+ "centroidDifference": [
+ 17.115453184570185,
+ -0.0254719602617115
+ ],
+ "majorScaleRatio": 0.9898375098828345,
+ "minorScaleRatio": 0.9906125847184443,
+ "angleDifferenceDegrees": -1.2384816899117368,
+ "centroidCorrectionL1": 0.24168684072871796,
+ "before": 0.12498652413707272,
+ "translation": {
+ "parameters": [
+ -0.375,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.11905000328198519,
+ "normalizedL1": 0.11946323417175603
+ },
+ "similarity": {
+ "parameters": [
+ -0.375,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.11905000328198519,
+ "normalizedL1": 0.11946323417175603
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00119; not a statistical confidence interval",
+ "ranges": [
+ [
+ -0.375,
+ -0.125
+ ],
+ [
+ -0.25,
+ -0.25
+ ],
+ [
+ 0.999,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "optimized",
+ "lens": "neutral",
+ "seam": "yz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 397.593091066362,
+ 402.96972849996547
+ ],
+ "sigmaMajor": 94.12165459748606,
+ "sigmaMinor": 90.80739516665696,
+ "angleDegrees": -7.997224192515963,
+ "mass": 5657863
+ },
+ "b": {
+ "centroid": [
+ 397.87827266566774,
+ 385.54498284225355
+ ],
+ "sigmaMajor": 93.80367743434813,
+ "sigmaMinor": 89.5135194248101,
+ "angleDegrees": -4.50644955800141,
+ "mass": 5638561
+ },
+ "centroidDifference": [
+ 0.2851815993057585,
+ -17.424745657711924
+ ],
+ "majorScaleRatio": 0.9966216364927097,
+ "minorScaleRatio": 0.9857514276290797,
+ "angleDifferenceDegrees": 3.490774634514537,
+ "centroidCorrectionL1": 0.2175045113566766,
+ "before": 0.1146546907233652,
+ "translation": {
+ "parameters": [
+ 0,
+ 0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.11068049330845652,
+ "normalizedL1": 0.11165856557792095
+ },
+ "similarity": {
+ "parameters": [
+ 0,
+ 0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.11068049330845652,
+ "normalizedL1": 0.11165856557792095
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00111; not a statistical confidence interval",
+ "ranges": [
+ [
+ 0,
+ 0
+ ],
+ [
+ -0.25,
+ 0.25
+ ],
+ [
+ 0.999,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "optimized",
+ "lens": "eso1119b",
+ "seam": "xz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 348.8404264555682,
+ 356.10333499582697
+ ],
+ "sigmaMajor": 93.75723106576999,
+ "sigmaMinor": 84.00601905091592,
+ "angleDegrees": -44.27316541086564,
+ "mass": 5255504.852999933
+ },
+ "b": {
+ "centroid": [
+ 366.30404691009835,
+ 356.05381611125966
+ ],
+ "sigmaMajor": 92.76745790081176,
+ "sigmaMinor": 83.20223999951726,
+ "angleDegrees": -45.40976432409586,
+ "mass": 5281926.687399859
+ },
+ "centroidDifference": [
+ 17.463620454530144,
+ -0.04951888456730558
+ ],
+ "majorScaleRatio": 0.9894432338315974,
+ "minorScaleRatio": 0.9904318873756951,
+ "angleDifferenceDegrees": -1.1365989132302161,
+ "centroidCorrectionL1": 0.2271038495502499,
+ "before": 0.12007917840015177,
+ "translation": {
+ "parameters": [
+ -0.375,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.1143281767983957,
+ "normalizedL1": 0.11472831153466405
+ },
+ "similarity": {
+ "parameters": [
+ -0.375,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.1143281767983957,
+ "normalizedL1": 0.11472831153466405
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00114; not a statistical confidence interval",
+ "ranges": [
+ [
+ -0.375,
+ -0.125
+ ],
+ [
+ -0.5,
+ -0.25
+ ],
+ [
+ 0.999,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "optimized",
+ "lens": "eso1119b",
+ "seam": "yz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 397.4228859654032,
+ 403.47034023025765
+ ],
+ "sigmaMajor": 92.06082180564914,
+ "sigmaMinor": 89.1278788451523,
+ "angleDegrees": -8.631044334871223,
+ "mass": 5552954.807999948
+ },
+ "b": {
+ "centroid": [
+ 397.7072989000036,
+ 385.7109977699769
+ ],
+ "sigmaMajor": 91.72455513916269,
+ "sigmaMinor": 87.84865242187685,
+ "angleDegrees": -4.666154201828294,
+ "mass": 5534213.131799823
+ },
+ "centroidDifference": [
+ 0.2844129346003683,
+ -17.75934246028072
+ ],
+ "majorScaleRatio": 0.9963473423342195,
+ "minorScaleRatio": 0.9856472919601517,
+ "angleDifferenceDegrees": 3.9648901330429567,
+ "centroidCorrectionL1": 0.20529843801663494,
+ "before": 0.11017354402412256,
+ "translation": {
+ "parameters": [
+ 0,
+ 0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.10635553793655905,
+ "normalizedL1": 0.10727774901039394
+ },
+ "similarity": {
+ "parameters": [
+ 0,
+ 0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.10635553793655905,
+ "normalizedL1": 0.10727774901039394
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00106; not a statistical confidence interval",
+ "ranges": [
+ [
+ -0.25,
+ 0
+ ],
+ [
+ -0.25,
+ 0.5
+ ],
+ [
+ 0.999,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "optimized",
+ "lens": "eso0844a",
+ "seam": "xz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 348.79878913082865,
+ 355.64151066550767
+ ],
+ "sigmaMajor": 95.52900388040025,
+ "sigmaMinor": 85.68414938489464,
+ "angleDegrees": -46.607260649380784,
+ "mass": 5253982.726199868
+ },
+ "b": {
+ "centroid": [
+ 366.28982358738483,
+ 355.611838394766
+ ],
+ "sigmaMajor": 94.5379945326683,
+ "sigmaMinor": 84.911656929135,
+ "angleDegrees": -47.7641435349224,
+ "mass": 5279093.931799817
+ },
+ "centroidDifference": [
+ 17.49103445655618,
+ -0.02967227074168477
+ ],
+ "majorScaleRatio": 0.9896260893814756,
+ "minorScaleRatio": 0.9909844182231466,
+ "angleDifferenceDegrees": -1.15688288554162,
+ "centroidCorrectionL1": 0.23421718262299918,
+ "before": 0.1238774224552733,
+ "translation": {
+ "parameters": [
+ -0.375,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.1179593119169893,
+ "normalizedL1": 0.11840639066505237
+ },
+ "similarity": {
+ "parameters": [
+ -0.375,
+ -0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.1179593119169893,
+ "normalizedL1": 0.11840639066505237
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00118; not a statistical confidence interval",
+ "ranges": [
+ [
+ -0.375,
+ -0.125
+ ],
+ [
+ -0.5,
+ -0.25
+ ],
+ [
+ 0.999,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ },
+ {
+ "version": "optimized",
+ "lens": "eso0844a",
+ "seam": "yz",
+ "dimensions": [
+ 800,
+ 700
+ ],
+ "a": {
+ "centroid": [
+ 397.2740654720268,
+ 403.0263849456587
+ ],
+ "sigmaMajor": 93.72698785562304,
+ "sigmaMinor": 90.32004154546686,
+ "angleDegrees": -8.668017335961613,
+ "mass": 5551236.546599844
+ },
+ "b": {
+ "centroid": [
+ 397.56666618733203,
+ 385.2579220114578
+ ],
+ "sigmaMajor": 93.40694523774904,
+ "sigmaMinor": 88.96565536222752,
+ "angleDegrees": -5.419780461398684,
+ "mass": 5531989.493999747
+ },
+ "centroidDifference": [
+ 0.292600715305241,
+ -17.768462934200898
+ ],
+ "majorScaleRatio": 0.9965853739120798,
+ "minorScaleRatio": 0.9850045885712138,
+ "angleDifferenceDegrees": 3.248236874562906,
+ "centroidCorrectionL1": 0.2118471151705653,
+ "before": 0.11369655642048106,
+ "translation": {
+ "parameters": [
+ 0,
+ 0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.10979998550202132,
+ "normalizedL1": 0.1107583061021469
+ },
+ "similarity": {
+ "parameters": [
+ 0,
+ 0.25,
+ 1,
+ 0
+ ],
+ "coarseL1": 0.10979998550202132,
+ "normalizedL1": 0.1107583061021469
+ },
+ "uncertainty": {
+ "criterion": "conditional one-parameter cost <= optimum + 0.00110; not a statistical confidence interval",
+ "ranges": [
+ [
+ -0.25,
+ 0
+ ],
+ [
+ -0.25,
+ 0.5
+ ],
+ [
+ 0.999,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/oblique.png b/labs/nebula/models/omega-centauri/evidence/2026-09-20/oblique.png
new file mode 100644
index 0000000000..6bc9f33ee4
Binary files /dev/null and b/labs/nebula/models/omega-centauri/evidence/2026-09-20/oblique.png differ
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/shape-moments.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/shape-moments.json
new file mode 100644
index 0000000000..1aa6d727ae
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/shape-moments.json
@@ -0,0 +1,271 @@
+{
+ "note": "Luminosity-weighted second moments within retained smooth-envelope domain, not an observed isophotal axis ratio. Finite residuals outside this domain excluded; no browser compositing included.",
+ "results": [
+ {
+ "grid": 96,
+ "bounds": {
+ "min": [
+ -1650,
+ -1650,
+ -1445.7971390624998
+ ],
+ "max": [
+ 1650,
+ 1650,
+ 1445.7971390625007
+ ]
+ },
+ "prior": {
+ "center": [
+ 1.560481538095193e-14,
+ -3.459767245096241e-15,
+ 2.3452126415096068e-14
+ ],
+ "coordinateSigma": [
+ 377.7350747251341,
+ 353.9585461091982,
+ 348.3259827365284
+ ],
+ "principalSigma": [
+ 378.57419469745133,
+ 373.12019227940203,
+ 326.7481147815035
+ ],
+ "axisRatios": [
+ 1,
+ 0.985593306425949,
+ 0.8631019212565025
+ ],
+ "covariance": [
+ [
+ 142683.78667760265,
+ 3371.564463469337,
+ 2789.866912734906
+ ],
+ [
+ 3371.564463469337,
+ 125286.65236373738,
+ -15901.466353376338
+ ],
+ [
+ 2789.8669127349062,
+ -15901.46635337634,
+ 121330.99024936826
+ ]
+ ]
+ },
+ "envelope": {
+ "center": [
+ 4.215612188306252,
+ -15.773196932554809,
+ 1.3678104890428269
+ ],
+ "coordinateSigma": [
+ 475.7979245365697,
+ 452.2548906928312,
+ 426.59283941627734
+ ],
+ "principalSigma": [
+ 477.94296305848906,
+ 469.3989965223332,
+ 405.13541923267104
+ ],
+ "axisRatios": [
+ 1,
+ 0.9821234599177261,
+ 0.8476647854381987
+ ],
+ "covariance": [
+ [
+ 226383.6649933073,
+ 6329.240716223733,
+ 4288.49503992903
+ ],
+ [
+ 6329.240716223732,
+ 204534.48615558472,
+ -25979.367594674786
+ ],
+ [
+ 4288.495039929031,
+ -25979.36759467478,
+ 181981.45064124177
+ ]
+ ]
+ },
+ "full": {
+ "center": [
+ 3.616111786215908,
+ -15.909294456611955,
+ -2.9470002101622432
+ ],
+ "coordinateSigma": [
+ 516.4717446767993,
+ 494.99507860693444,
+ 445.1480537164255
+ ],
+ "principalSigma": [
+ 519.7210945646997,
+ 503.3514750031591,
+ 431.79538800075034
+ ],
+ "axisRatios": [
+ 1,
+ 0.9685030687945211,
+ 0.8308213626818576
+ ],
+ "covariance": [
+ [
+ 266743.06304949697,
+ 8870.224150909537,
+ 5515.008712836372
+ ],
+ [
+ 8870.224150909544,
+ 245020.1278450852,
+ -24936.234145604052
+ ],
+ [
+ 5515.00871283637,
+ -24936.234145604052,
+ 198156.78972752168
+ ]
+ ]
+ },
+ "envelopeLightFraction": 0.831805412682356
+ },
+ {
+ "grid": 144,
+ "bounds": {
+ "min": [
+ -1650,
+ -1650,
+ -1445.7971390624998
+ ],
+ "max": [
+ 1650,
+ 1650,
+ 1445.7971390625007
+ ]
+ },
+ "prior": {
+ "center": [
+ -1.0151363512695027e-14,
+ -1.1295333611857842e-14,
+ 8.495717172413022e-14
+ ],
+ "coordinateSigma": [
+ 377.73208386631586,
+ 353.95558098718703,
+ 348.3233577621448
+ ],
+ "principalSigma": [
+ 378.57119637029183,
+ 373.1172713079454,
+ 326.74545626089537
+ ],
+ "axisRatios": [
+ 1,
+ 0.9855933966592328,
+ 0.8631017346108283
+ ],
+ "covariance": [
+ [
+ 142681.52718198948,
+ 3371.5211479747,
+ 2789.830771669299
+ ],
+ [
+ 3371.5211479747,
+ 125284.55331197711,
+ -15901.260233418061
+ ],
+ [
+ 2789.8307716693,
+ -15901.260233418065,
+ 121329.16156269511
+ ]
+ ]
+ },
+ "envelope": {
+ "center": [
+ 4.205158445769136,
+ -15.795395375786287,
+ 1.3686883372312424
+ ],
+ "coordinateSigma": [
+ 475.80796862043405,
+ 452.3012411177898,
+ 426.59886607537175
+ ],
+ "principalSigma": [
+ 477.94468914209943,
+ 469.4394736931407,
+ 405.15636847488753
+ ],
+ "axisRatios": [
+ 1,
+ 0.982204603080274,
+ 0.8477055560594985
+ ],
+ "covariance": [
+ [
+ 226393.22300270395,
+ 6311.296069270738,
+ 4289.195136480313
+ ],
+ [
+ 6311.296069270736,
+ 204576.412716693,
+ -25980.945410323246
+ ],
+ [
+ 4289.195136480313,
+ -25980.945410323246,
+ 181986.59253679294
+ ]
+ ]
+ },
+ "full": {
+ "center": [
+ 6.868789125621971,
+ -12.58320475479186,
+ -1.9591667680681792
+ ],
+ "coordinateSigma": [
+ 521.304156851149,
+ 498.4532188336728,
+ 448.4064223227182
+ ],
+ "principalSigma": [
+ 521.810586326594,
+ 509.39762780082117,
+ 435.3271455866139
+ ],
+ "axisRatios": [
+ 1,
+ 0.9762117541287217,
+ 0.8342627708096145
+ ],
+ "covariance": [
+ [
+ 271758.0239502873,
+ 3335.2962413424134,
+ 5210.7485504650795
+ ],
+ [
+ 3335.2962413424143,
+ 248455.61136564933,
+ -25486.045064795053
+ ],
+ [
+ 5210.748550465077,
+ -25486.045064795057,
+ 201068.3195802599
+ ]
+ ]
+ },
+ "envelopeLightFraction": 0.8283077726791853
+ }
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/side-x.png b/labs/nebula/models/omega-centauri/evidence/2026-09-20/side-x.png
new file mode 100644
index 0000000000..7eadd856e0
Binary files /dev/null and b/labs/nebula/models/omega-centauri/evidence/2026-09-20/side-x.png differ
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/side-y.png b/labs/nebula/models/omega-centauri/evidence/2026-09-20/side-y.png
new file mode 100644
index 0000000000..4a314d90db
Binary files /dev/null and b/labs/nebula/models/omega-centauri/evidence/2026-09-20/side-y.png differ
diff --git a/labs/nebula/models/omega-centauri/evidence/2026-09-20/validation.json b/labs/nebula/models/omega-centauri/evidence/2026-09-20/validation.json
new file mode 100644
index 0000000000..6d2bc9f7ee
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/evidence/2026-09-20/validation.json
@@ -0,0 +1,429 @@
+{
+ "testedBase": "ef1e8b067f8f7d23d4bf13ea065f02f1dec7f4ad",
+ "testedState": "Working tree with the source hashes below, not the base commit alone. Publication only updates documentation/evidence and excludes the unfinished app object and its source-catalogue entries.",
+ "reuse": "Published authored implementations exactly match the reviewed/tested source checkpoint. Documentation, images and retaining reports do not change their behavior. No native or compact Omega application delivery is claimed.",
+ "sourcePins": [
+ {
+ "path": ".github/workflows/universe.yml",
+ "bytes": 36908,
+ "sha256": "6fab0b9cb34fecf7056fb2626abff3df0644c7400f476a69bac08662791402a6"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/candidates.json",
+ "bytes": 170,
+ "sha256": "be78206e67c31d36698c69818a383e922c48603cb157bbd31e8bcf824bb24240"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/compiler.json",
+ "bytes": 1361,
+ "sha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/native-registration.json",
+ "bytes": 3895,
+ "sha256": "ef6e7213e2e95bb6f5ab7f1c9e080dce8ebc0e0d65f9897b239541feefab0b96"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/observation-structures.json",
+ "bytes": 397,
+ "sha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/observations.json",
+ "bytes": 3842,
+ "sha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/photometric-mge.json",
+ "bytes": 2508,
+ "sha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/physical-evidence.json",
+ "bytes": 36711,
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/verify-registration.mts",
+ "bytes": 6801,
+ "sha256": "1c3d9fd284944dedf2751204a193a5c05dc1cda00813e60a9e0c2ed1f0ded18b"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler-export.ts",
+ "bytes": 4190,
+ "sha256": "842bbf1dca3107a790a792cd5f4a93713f7a739a80e522f58d976953b3082c1c"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.test.ts",
+ "bytes": 5215,
+ "sha256": "e68540664c6863bb72a0516c04ca78e1171357cb85320102c87c051f0e171859"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.test.ts",
+ "bytes": 3549,
+ "sha256": "06bdb5db40ad175e8beda298d845460ff3a6bced3fccbfb6999cc1b1852b24f9"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/delivery/nebula-frame.test.ts",
+ "bytes": 6412,
+ "sha256": "1cc99841a584798d1cc860037211e99f4b05069abeda2806ad371dacc891be0e"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/viewer/compiler-renderer.ts",
+ "bytes": 1664,
+ "sha256": "a257b20d6272c9c1f7f6a4168e99cf97a74d18c4419c1e96babe93a5e238c852"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/viewer/shape-cloud-camera.test.ts",
+ "bytes": 9398,
+ "sha256": "f4fa9ada51112c39df61620d78250bd88ffee3ec87db789b1c3dadbfa54ab1ff"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/viewer/shape-cloud-renderer.ts",
+ "bytes": 1825,
+ "sha256": "a3d3d6397dcae176019c26a6ac81831ff981979b6f1dd54bfaa479191d73eb70"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/viewer/volume-renderer.ts",
+ "bytes": 4657,
+ "sha256": "1ea073d746c166a2ba288e70f9ed28ca2659568b141a4d915d9f896d274d8bfb"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/cli/commands/compile-candidates.ts",
+ "bytes": 9831,
+ "sha256": "5a17c30851c48282fa868a63103fd886dfe8c6e094055ccf11661371299269cb"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/features/compiler/compiler-panel.tsx",
+ "bytes": 16860,
+ "sha256": "46503a20b65c49d6b8e95d2d87108bef82c1f8193b1a744a920b7e88d0c99233"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/features/compiler/compiler-published.test.ts",
+ "bytes": 15744,
+ "sha256": "935df76c344d0ac162b8ebc09ccb628e6080ff515eab253ee24b6849d1c6acfc"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/features/compiler/compiler-published.ts",
+ "bytes": 11622,
+ "sha256": "a393451facf57d091604e7f257580a4d5a546af5f13b6417509814676cfccebd"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/features/compiler/model.ts",
+ "bytes": 9060,
+ "sha256": "53985ebe1a3ebe6e22a1044329dfac4f5c21515550b9b3332bc3dc899eb0fa40"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.test.ts",
+ "bytes": 3652,
+ "sha256": "22fde1c27c62a73c0a4ff5ab386911feab7537c92a335233f4760e27dc707aeb"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.ts",
+ "bytes": 2041,
+ "sha256": "18c23ab70cb082b88b7c17465c8c780be775df1cc44b7bf17d265b98f6f3a5a3"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud.css",
+ "bytes": 6350,
+ "sha256": "54407d03391fcbc71cc5f98a3f09f741e1e76ee3518e4f7d96eae401adc51321"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/resources/model-catalogue.test.ts",
+ "bytes": 3569,
+ "sha256": "1966cacc164166ce3375713508ca08d2806712c0b5ae7b76b465966d0fd4bfb8"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/bake.test.ts",
+ "bytes": 5795,
+ "sha256": "78e91cab5b70cbfcea8a4bc3f49593436b653dad898710cc1f96c94358571e4b"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/compile.ts",
+ "bytes": 21267,
+ "sha256": "7b484fcb57e5752c91325d70fb2cb55e31419f8846345fad2289d646050b9fb9"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/compiler-defaults.test.ts",
+ "bytes": 3888,
+ "sha256": "f356421a5db71883e9e18fa2cd233752a69d3ad26211d64b1b2ecd9ec39afe00"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/photometric-prior.test.ts",
+ "bytes": 7965,
+ "sha256": "bfc52b540126d441e19db4c0f12f9fddbf0300f45b83ff32bd55357d6b394db1"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/photometric-prior.ts",
+ "bytes": 7847,
+ "sha256": "1c8fd7c75a48670a009553f84c7cedd167cf3f51d8119b688006d8e62ec03f46"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/refresh-stars.ts",
+ "bytes": 8252,
+ "sha256": "640c95efa09083f1a51f63afd4b938deec2663742f7ded90b42c8128421e3345"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/retained-material-bank.test.ts",
+ "bytes": 8530,
+ "sha256": "16c48b1151fdf6f69774cc3279c69dbd5afba08481bc122c1ef8b8be020cb4a0"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/observations/fixtures/omega-centauri-native-stars.json",
+ "bytes": 9021,
+ "sha256": "5c5e98214276619d95853fdead319f8eaec87b8cc086c24a3838818eec693037"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/observations/registration.test.ts",
+ "bytes": 7994,
+ "sha256": "252e8fb86c41007106abe9f1e3184d95f5291d7ce6f1d3dd84a08b5e9df06598"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/sampled-prior/compile.ts",
+ "bytes": 22136,
+ "sha256": "86279d10312c87d3368742a372125ee1131dc1b5b7e2a11318ce8a2f1d784fd7"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/sampled-prior/component-banks.test.ts",
+ "bytes": 6568,
+ "sha256": "304b44a663e059ac0bdff63148b96abb6a0db22f53702ea1435cf2deb0671d6c"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/sampled-prior/layout.test.ts",
+ "bytes": 6904,
+ "sha256": "fc96c9bcb2a2427c6166fb38566b8a1b197f431a9f674b8c173f49d4308d37dc"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/state/subjects.json",
+ "bytes": 22793,
+ "sha256": "2d6f6c42cafe75881a19f57fcdf24780495d12e2e68f881b2d26c797e7659284"
+ },
+ {
+ "path": "labs/nebula/packages/reconstruction/package.json",
+ "bytes": 6818,
+ "sha256": "5fa80cc19bd82e22d7499982ec8006e9d6696f2a618a398c6a60b00ee15af33d"
+ },
+ {
+ "path": "labs/nebula/packages/reconstruction/src/methods/inference/fit.ts",
+ "bytes": 19436,
+ "sha256": "67c2c671c6a3cc8f73f26ad99e142058f478477ed944e785fb68de0b6a4f6fb3"
+ },
+ {
+ "path": "labs/nebula/packages/reconstruction/src/methods/inference/photometric-mge.test.ts",
+ "bytes": 5573,
+ "sha256": "a5819a1149c665c94083b3155abafa6f9a9c1050cd2c03a5312241c59f2ea600"
+ },
+ {
+ "path": "labs/nebula/packages/reconstruction/src/methods/inference/photometric-mge.ts",
+ "bytes": 671,
+ "sha256": "13109dd3f1d28dd15c3d733f737753c15d0c85e3e2ae4e3e345226f6f9cfe430"
+ },
+ {
+ "path": "labs/nebula/packages/reconstruction/src/methods/inference/simulation-guided.ts",
+ "bytes": 11533,
+ "sha256": "e9d509a5a0fe46621ffd3bd98c0f50188eaa49c49657a444a5de5394d18ea955"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts",
+ "bytes": 8729,
+ "sha256": "a9c3ebe7c2e88a8b3c624be2c0fef4f00f0b3d6b011809646486e1a6f7c3085a"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compact-inputs/sampled.ts",
+ "bytes": 11992,
+ "sha256": "1fcd2d1db257d0f7d7e45e04c9660373f5f019e9329d62e8a6f27d6e6834ea73"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/bake.ts",
+ "bytes": 18347,
+ "sha256": "61419c3023157f4666cfd23a9edafbf749c84c79bfefdcd165506c55f224c719"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/compiler-sampling.test.ts",
+ "bytes": 5318,
+ "sha256": "85b518a3644ca5dbf7a49a70aef754e76949fce610ecfe7ff6c0a9753dc8afff"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/component-layout.ts",
+ "bytes": 16703,
+ "sha256": "7466f8813cf5a2684e9633624bd6d811bb6de9e49ba9a1a3921d4b6e6450512d"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/retained-material.ts",
+ "bytes": 10547,
+ "sha256": "59756ab35b9fcc3e249977a36521d99ee38c9020666b3866d40e6464d0d5852c"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/slices/emission-layout.test.ts",
+ "bytes": 6642,
+ "sha256": "e38597b85ea59a2fef154660f9df07dfcc755dc5d23d61d3633324b4497b0696"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/slices/emission.ts",
+ "bytes": 21178,
+ "sha256": "febe3fc0821a0ca82a4c04d19c0771744cec20fa078bb75a82de3fef043869d7"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/slices/material.ts",
+ "bytes": 10360,
+ "sha256": "dcb0c1b4524eb77ed9547a369ce5a24ede9e298af1ed507e3cfb1e504f5aff96"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/package.json",
+ "bytes": 4015,
+ "sha256": "5c82ed209bf29c8f3eefe61dd8cb0244d57d647a67fb94db10249dc09090a94e"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts",
+ "bytes": 12561,
+ "sha256": "2488b91bd967064028a552fd58593e3d26eddc77327e9377a4e0a0b5e1bdd3f6"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/emission.ts",
+ "bytes": 4057,
+ "sha256": "712c11507af42b9f111f17ec0ceee9023a8eed29fac1700577bb88fe5cc47140"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/volume-slices.ts",
+ "bytes": 6642,
+ "sha256": "5965a3ac2d6acef6d13ea625df2456960b9197fd4dcfccf7ea8b5b2ec452fda3"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/coordinates/compiler-frame-layers.test.ts",
+ "bytes": 3892,
+ "sha256": "ac57880e7491ae0441b3b0bc67fe1c0a013fbea1177270bb10fe8d10426f9e4d"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/coordinates/compiler-frame.ts",
+ "bytes": 3834,
+ "sha256": "e340a3eb8f6a374556aa4277cb3ee2064a1ebedd906a012e94f7921d2f4b4d3b"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/fields/photometric-emission.ts",
+ "bytes": 5761,
+ "sha256": "af6e9cab101e246f3f840a122a5e4d08a7fce0e49600881a9486db46720ff9fb"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/fields/photometric-mge.ts",
+ "bytes": 6744,
+ "sha256": "2865d5349871c0e9c3d18d677062e1c700afa55bc7b1b18ad385b4c748d02618"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/fields/retained-emission.ts",
+ "bytes": 2784,
+ "sha256": "0b621e41516dbeb8b37da031dd6f07251ff0bf3498233653cbee81dc491d4f84"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/fields/simulation-envelope.ts",
+ "bytes": 13256,
+ "sha256": "07c8b4c90ebc3cc46084cd607b2ea4ebfd95879cc2645ddc1988f85839a2733e"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/materials/material.test.ts",
+ "bytes": 13125,
+ "sha256": "5b188f9f8c2d55567261747e3b534521dcfb6997010fe78ef6533ad75fc5e525"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/materials/slab-material.ts",
+ "bytes": 13459,
+ "sha256": "f5aebe26a52df5e7fde3fbf404e2505d30f990cfaa4e0a2ecbfcdac0cdfbcda1"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/sampling/layer-optimization.test.ts",
+ "bytes": 5946,
+ "sha256": "53286e81a79a6e4fc6863ad33bfe172240260c72c691c786c7701643e4c926d1"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/sampling/layer-optimization.ts",
+ "bytes": 13031,
+ "sha256": "98c9ac63b39b4f1e8ff2fab6b3d6beb5423033fa4a5a9fe5048b54c862b4918b"
+ },
+ {
+ "path": "packages/catalog/src/nebulae.test.ts",
+ "bytes": 3486,
+ "sha256": "3a38bb21caaeda12ff487d77fc58e376e14439b9cc7ceaf33d6a39104aa314af"
+ },
+ {
+ "path": "packages/catalog/src/nebulae.ts",
+ "bytes": 4861,
+ "sha256": "5b6f5c2da9ba90a73a05e7080606d7ba213c9317976aa15043c00ec26ed70d99"
+ },
+ {
+ "path": "site/prepared-focus-card.mts",
+ "bytes": 7880,
+ "sha256": "2041c07acc18088cda61b68d5ae0e02f318a1f1a799a49fe0d1bb009c8b8570c"
+ },
+ {
+ "path": "site/prepared-focus-object.mts",
+ "bytes": 2334,
+ "sha256": "4ca6d397fe6bb700c6a221d059c72e5b03de30a5237debcbf51f3490a25ccfd0"
+ },
+ {
+ "path": "site/test/galactic-focus.test.mts",
+ "bytes": 2309,
+ "sha256": "cab90564f2ab50aa0b5f40a45573570c57d2220bfe930e0e2b73cdeaa351b184"
+ },
+ {
+ "path": "site/test/prepared-focus-card.test.mts",
+ "bytes": 9363,
+ "sha256": "8de40ffaa5d1dfb52287c551ab26e9db6a943cd04690321cc098976d5efbd3cd"
+ },
+ {
+ "path": "src/renderers/css/universe/catalog-marker.test.ts",
+ "bytes": 2488,
+ "sha256": "743539f43896c9c5737ca6fbb7b0db934da1cbc8f75375710f5ea1d8438ace91"
+ },
+ {
+ "path": "src/renderers/css/universe/catalog-marker.ts",
+ "bytes": 2390,
+ "sha256": "c2159ff0ed567d74ff614dd7e3b39b702af1c586a4ae73be73a18814846ff119"
+ },
+ {
+ "path": "tools/nebula/application/nebula-frame.ts",
+ "bytes": 5029,
+ "sha256": "382e115799a417d2e09d1b43067dd31fadf2a32dc643b0e840195f39b14d8528"
+ },
+ {
+ "path": "tools/nebula/application/objects.ts",
+ "bytes": 22751,
+ "sha256": "3f2b3939e80769a7285b8677271939954ecbeb36f689d5e08731100c8807796c"
+ },
+ {
+ "path": "tools/prepare-navigation-destinations.mts",
+ "bytes": 4439,
+ "sha256": "d406c51f5b761134b39446f0d1935a22e5c8829913d520132385465974777671"
+ }
+ ],
+ "fullLabSuite": {
+ "command": "node labs/nebula/run.mts test",
+ "tests": 681,
+ "passed": 659,
+ "skipped": 17,
+ "failed": 5,
+ "failures": [
+ "# Subtest: delivery restores missing impostors, rejects drift and rebuilds when their generator changes\nnot ok 32 - delivery restores missing impostors, rejects drift and rebuilds when their generator changes\n ---\n duration_ms: 289.361875\n type: 'test'\n location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-objects.test.mjs:14775:1'\n failureType: 'testCodeFailure'\n error: |-\n Cannot find module '@cssearth/volume-bake/package.json'\n Require stack:\n - /var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/nebula-impostor-delivery-WCZmfC/package.json\n code: 'MODULE_NOT_FOUND'\n stack: |-\n Function._resolveFilename (node:internal/modules/cjs/loader:1430:15)\n Function.resolve (node:internal/modules/helpers:157:19)\n packageImplementationPins (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-objects.test.mjs:3862:35)\n prepareNebulaObject (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-objects.test.mjs:8461:29)\n async TestContext. (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-objects.test.mjs:14779:20)\n async Test.run (node:internal/test_runner/test:1054:7)\n async Test.processPendingSubtests (node:internal/test_runner/test:744:7)\n ...",
+ "# Subtest: the curated lab retains the selected LMC images, SMC comparisons and verified density banks\nnot ok 179 - the curated lab retains the selected LMC images, SMC comparisons and verified density banks\n ---\n duration_ms: 7.521917\n type: 'test'\n location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/resources/model-catalogue.test.mjs:32:1'\n failureType: 'testCodeFailure'\n error: \"ENOENT: no such file or directory, open '/private/tmp/cssearth-pr390/labs/nebula/models/lmc/candidates/prepared/vista-infrared.webp'\"\n code: 'ENOENT'\n stack: |-\n async open (node:internal/fs/promises:639:25)\n async readFile (node:internal/fs/promises:1252:14)\n async TestContext. (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/resources/model-catalogue.test.mjs:54:19)\n async Test.run (node:internal/test_runner/test:1054:7)\n async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3)\n ...",
+ "# Subtest: tiny offline bake uses shared density support and writes pinned XYZ resources and cloud inspection\nnot ok 248 - tiny offline bake uses shared density support and writes pinned XYZ resources and cloud inspection\n ---\n duration_ms: 1265.759375\n type: 'test'\n location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workers/density-reconstruction.test.mjs:2743:1'\n failureType: 'testCodeFailure'\n error: \"ENOENT: no such file or directory, open '/private/tmp/cssearth-pr390/labs/nebula/models/lmc/full-density/prepared/slices/x/00.webp'\"\n code: 'ENOENT'\n stack: |-\n async open (node:internal/fs/promises:639:25)\n async readFile (node:internal/fs/promises:1252:14)\n async recolorCloudSlices (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workers/density-reconstruction.test.mjs:725:31)\n async prepareReconstruction (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workers/density-reconstruction.test.mjs:2537:19)\n async TestContext. (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workers/density-reconstruction.test.mjs:2783:20)\n async Test.run (node:internal/test_runner/test:1054:7)\n async Test.processPendingSubtests (node:internal/test_runner/test:744:7)\n ...",
+ "# Subtest: all 943 prepared stars come from pinned measured V rows and preserve independent sky rays and native footprint\nnot ok 523 - all 943 prepared stars come from pinned measured V rows and preserve independent sky rays and native footprint\n ---\n duration_ms: 12.315666\n type: 'test'\n location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4766:1'\n failureType: 'testCodeFailure'\n error: \"ENOENT: no such file or directory, open 'labs/nebula/models/lmc/clouds-observation/source/target.png'\"\n code: 'ENOENT'\n stack: |-\n async open (node:internal/fs/promises:639:25)\n async readFile (node:internal/fs/promises:1252:14)\n async pinned (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4570:17)\n async file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4587:111\n async Promise.all (index 7)\n async loadStarCloudModel (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4587:38)\n ...",
+ "# Subtest: joint CDF preserves solid-angle weighting and rejects missing cloud/density and true support gaps\nnot ok 524 - joint CDF preserves solid-angle weighting and rejects missing cloud/density and true support gaps\n ---\n duration_ms: 0.514292\n type: 'test'\n location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4830:1'\n failureType: 'testCodeFailure'\n error: \"ENOENT: no such file or directory, open 'labs/nebula/models/lmc/clouds-observation/source/target.png'\"\n code: 'ENOENT'\n stack: |-\n async open (node:internal/fs/promises:639:25)\n async readFile (node:internal/fs/promises:1252:14)\n async pinned (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4570:17)\n async file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4587:111\n async Promise.all (index 7)\n async loadStarCloudModel (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4587:38)\n ...\n# (node:10482) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 3)\n# (Use `node --trace-warnings ...` to show where the warning was created)"
+ ],
+ "comparisonToMain": "Not independently reproduced on current main; not claimed identical to main."
+ },
+ "clean": [
+ "Core/bake/Lab typechecks and all 203 strict Lab test-source files",
+ "Nebula package boundaries",
+ "Nebula Lab Vite production build",
+ "52 focused planner/integration regressions and 17 component-registration regressions",
+ "Budget, default-planning, interval-reflection, material-offset, secondary-lens and empty-partition mutation checks fail as intended",
+ "Historical no-plan manifest and PNG golden digest unchanged"
+ ],
+ "independentReview": "Grok subscription review packets cover planner, integration, grouped sampling, component registration and final cleanup. All completed with sentinels and source snapshots; code findings closed, rendered-quality failures remain open.",
+ "visualQualification": "FAILED: Omega X/Z and Y/Z normalized-L1 handoffs exceed 0.04; no threshold was raised.",
+ "omitted": [
+ "Main-page Omega render and browser conformance",
+ "Cold Omega compact application replay",
+ "Omega R2 publication and clean asset restoration",
+ "Acceptance of the optimized Omega or Crab banks"
+ ],
+ "screenshots": "Four newly captured views illustrate the actual retained viewer. They are not before/after pixel comparisons; no Pixelmatch score is claimed."
+}
diff --git a/labs/nebula/models/omega-centauri/king-abel-derivation.mts b/labs/nebula/models/omega-centauri/king-abel-derivation.mts
new file mode 100644
index 0000000000..da18b6bc1c
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/king-abel-derivation.mts
@@ -0,0 +1,129 @@
+// Derive a spherically symmetric 3D emissivity profile for Omega Centauri (NGC 5139) by
+// Abel-inverting a single-mass King (1962) projected surface-density profile whose two shape
+// parameters are fixed from measured/published-model structural radii (Baumgardt & Hilker 2018).
+//
+// This is a data-preparation script, not lab reconstruction code: it produces a small evidence
+// table (radius, 3D emissivity) that a future baking capability would consume. All approximations
+// used are stated explicitly and carried into physical-evidence.json.
+
+const rc = 4.54; // pc, measured 3D King core radius (Baumgardt & Hilker 2018 / Baumgardt et al. 2021)
+const rh3D = 10.42; // pc, measured 3D half-mass radius (same source)
+
+// Approximation A (authored): treat the empirical King (1962) projected-profile core radius as
+// equal to the measured 3D core radius. This is the standard simplification used when only 3D
+// structural radii (not a separate photometric King fit) are available.
+//
+// Approximation B (published-model): projected half-light radius ~= 0.75 x 3D half-mass radius,
+// a well known approximate relation for a broad family of dynamical models (Wolf et al. 2010;
+// Spitzer 1987 discusses the same order-unity ratio).
+const projectedHalfLightTarget = 0.75 * rh3D; // pc
+
+function sigmaShape(Rpc: number, q: number): number {
+ // King (1962) empirical surface-density shape (unnormalized): [ (1+x^2)^-1/2 - (1+q^2)^-1/2 ]^2
+ const x = Rpc / rc;
+ if (x >= q) return 0;
+ const g = 1 / Math.sqrt(1 + q * q);
+ const f = 1 / Math.sqrt(1 + x * x) - g;
+ return f > 0 ? f * f : 0;
+}
+
+function dSigmaShape_dR(Rpc: number, q: number): number {
+ const x = Rpc / rc;
+ if (x >= q) return 0;
+ const g = 1 / Math.sqrt(1 + q * q);
+ const inv = 1 / Math.sqrt(1 + x * x);
+ const f = inv - g;
+ if (f <= 0) return 0;
+ const dfdx = -x * inv * inv * inv; // d/dx (1+x^2)^-1/2
+ const dfdR = dfdx / rc;
+ return 2 * f * dfdR;
+}
+
+function projectedHalfLightRadius(q: number): number {
+ const rt = q * rc;
+ const N = 4000, dR = rt / N;
+ let cum = 0;
+ const cumArr = new Float64Array(N + 1);
+ for (let i = 0; i <= N; i++) {
+ const R = i * dR;
+ cumArr[i] = cum;
+ // trapezoid increment for next step (Simpson-lite via midpoint refinement)
+ const Rm = R + dR / 2;
+ const mid = Rm <= rt ? sigmaShape(Rm, q) * Rm : 0;
+ cum += mid * dR; // midpoint rule increment for integral of Sigma(R) * 2piR dR (2pi cancels in ratio)
+ }
+ const total = cum;
+ const target = total / 2;
+ for (let i = 0; i < N; i++) {
+ if (cumArr[i] <= target && cumArr[i + 1] >= target) {
+ const t = (target - cumArr[i]) / (cumArr[i + 1] - cumArr[i] || 1);
+ return (i + t) * dR;
+ }
+ }
+ return rt;
+}
+
+// Solve for q = rt/rc via bisection so the projected half-light radius matches the target.
+let lo = 1.05, hi = 60;
+for (let iter = 0; iter < 80; iter++) {
+ const mid = (lo + hi) / 2;
+ const rHalf = projectedHalfLightRadius(mid);
+ if (rHalf < projectedHalfLightTarget) lo = mid; else hi = mid;
+}
+const q = (lo + hi) / 2;
+const rt = q * rc;
+const achievedHalf = projectedHalfLightRadius(q);
+console.error(JSON.stringify({ q, concentrationLog10: Math.log10(q), rt_pc: rt, achievedProjectedHalfLight_pc: achievedHalf, targetProjectedHalfLight_pc: projectedHalfLightTarget }));
+
+// Abel inversion via the r^2+u^2 substitution: rho(r) = -(1/pi) * integral_0^sqrt(rt^2-r^2) Sigma'(R(u)) du
+function rho(r: number): number {
+ if (r >= rt) return 0;
+ const uMax = Math.sqrt(rt * rt - r * r);
+ const N = 20000, du = uMax / N;
+ let acc = 0;
+ for (let i = 0; i < N; i++) {
+ const u = (i + 0.5) * du;
+ const R = Math.sqrt(r * r + u * u);
+ acc += (dSigmaShape_dR(R, q) / R) * du;
+ }
+ return -acc / Math.PI;
+}
+
+function rhoN(r: number, N: number): number {
+ if (r >= rt) return 0;
+ const uMax = Math.sqrt(rt * rt - r * r), du = uMax / N; let acc = 0;
+ for (let i = 0; i < N; i++) { const u = (i + 0.5) * du; const R = Math.sqrt(r * r + u * u); acc += (dSigmaShape_dR(R, q) / R) * du; }
+ return -acc / Math.PI;
+}
+for (const N of [2000, 8000, 32000, 128000]) {
+ console.error('convergence N=', N, 'rho(0.01)=', rhoN(0.01, N), 'rho(0.5)=', rhoN(0.5, N), 'rho(1.16)=', rhoN(1.16, N), 'rho(2)=', rhoN(2, N), 'rho(4.54)=', rhoN(4.54, N), 'rho(10)=', rhoN(10, N));
+}
+
+const NR = 200;
+const table = [];
+let peak = 0;
+for (let i = 0; i <= NR; i++) {
+ const r = (i / NR) * rt;
+ const v = Math.max(0, rho(r));
+ peak = Math.max(peak, v);
+ table.push(v);
+}
+const normalized = table.map(v => v / peak);
+
+const output = {
+ schema: 'omega-centauri-king-abel-profile@1',
+ description: 'Spherically symmetric 3D relative emissivity vs radius, Abel-inverted from a single-mass King (1962) projected surface-density shape.',
+ parameters: {
+ coreRadiusPc: rc, halfMassRadiusPc3D: rh3D, projectedHalfLightTargetPc: projectedHalfLightTarget,
+ solvedConcentrationRtOverRc: q, solvedConcentrationLog10: Math.log10(q), tidalRadiusPc: rt,
+ achievedProjectedHalfLightPc: achievedHalf
+ },
+ approximations: [
+ 'A: projected (2D) King core radius set equal to the measured 3D core radius (authored simplification; no independent photometric King fit was located for this recipe).',
+ 'B: projected half-light radius = 0.75 x 3D half-mass radius (Wolf et al. 2010-type relation; published-model, not object-specific).'
+ ],
+ radialSamples: NR + 1,
+ radiusPc: Array.from({ length: NR + 1 }, (_, i) => (i / NR) * rt),
+ relativeEmissivity: normalized
+};
+console.log(JSON.stringify(output));
diff --git a/labs/nebula/models/omega-centauri/king-abel-profile.json b/labs/nebula/models/omega-centauri/king-abel-profile.json
new file mode 100644
index 0000000000..22222c661c
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/king-abel-profile.json
@@ -0,0 +1,430 @@
+{
+ "schema": "cssearth-omega-centauri-king-abel-profile@1",
+ "evidenceId": "abel-inverted-emissivity",
+ "description": "Spherically symmetric 3D relative emissivity vs radius, Abel-inverted from a single-mass King (1962) projected surface-density shape.",
+ "derivation": "Single-mass King (1962) projected surface-density law, Sigma(R) = k[(1+(R/rc)^2)^-1/2 - (1+(rt/rc)^2)^-1/2]^2, deprojected via the standard Abel-inversion identity rho(r) = -(1/pi) * integral_r^rt (dSigma/dR) / sqrt(R^2-r^2) dR, evaluated numerically with a 20000-point midpoint quadrature per radius (verified convergent to 6+ significant figures against 2000/8000/32000/128000-point runs).",
+ "parameters": {
+ "coreRadiusPc": 4.54,
+ "halfMassRadiusPc3D": 10.42,
+ "projectedHalfLightTargetPc": 7.8149999999999995,
+ "solvedConcentrationRtOverRc": 10.221389884084143,
+ "solvedConcentrationLog10": 1.0095099543062203,
+ "tidalRadiusPc": 46.40511007374201,
+ "achievedProjectedHalfLightPc": 7.814999999999999
+ },
+ "approximations": [
+ "A: projected (2D) King core radius set equal to the measured 3D core radius (authored simplification; no independent photometric King fit was located for this recipe).",
+ "B: projected half-light radius = 0.75 x 3D half-mass radius (Wolf et al. 2010-type relation; published-model, not object-specific)."
+ ],
+ "radialSamples": 201,
+ "units": {
+ "radiusPc": "parsec from cluster center",
+ "relativeEmissivity": "peak-normalized to 1 at r=0; unitless display light, not calibrated luminosity or density"
+ },
+ "radiusPc": [
+ 0,
+ 0.232026,
+ 0.464051,
+ 0.696077,
+ 0.928102,
+ 1.160128,
+ 1.392153,
+ 1.624179,
+ 1.856204,
+ 2.08823,
+ 2.320256,
+ 2.552281,
+ 2.784307,
+ 3.016332,
+ 3.248358,
+ 3.480383,
+ 3.712409,
+ 3.944434,
+ 4.17646,
+ 4.408485,
+ 4.640511,
+ 4.872537,
+ 5.104562,
+ 5.336588,
+ 5.568613,
+ 5.800639,
+ 6.032664,
+ 6.26469,
+ 6.496715,
+ 6.728741,
+ 6.960767,
+ 7.192792,
+ 7.424818,
+ 7.656843,
+ 7.888869,
+ 8.120894,
+ 8.35292,
+ 8.584945,
+ 8.816971,
+ 9.048996,
+ 9.281022,
+ 9.513048,
+ 9.745073,
+ 9.977099,
+ 10.209124,
+ 10.44115,
+ 10.673175,
+ 10.905201,
+ 11.137226,
+ 11.369252,
+ 11.601278,
+ 11.833303,
+ 12.065329,
+ 12.297354,
+ 12.52938,
+ 12.761405,
+ 12.993431,
+ 13.225456,
+ 13.457482,
+ 13.689507,
+ 13.921533,
+ 14.153559,
+ 14.385584,
+ 14.61761,
+ 14.849635,
+ 15.081661,
+ 15.313686,
+ 15.545712,
+ 15.777737,
+ 16.009763,
+ 16.241789,
+ 16.473814,
+ 16.70584,
+ 16.937865,
+ 17.169891,
+ 17.401916,
+ 17.633942,
+ 17.865967,
+ 18.097993,
+ 18.330018,
+ 18.562044,
+ 18.79407,
+ 19.026095,
+ 19.258121,
+ 19.490146,
+ 19.722172,
+ 19.954197,
+ 20.186223,
+ 20.418248,
+ 20.650274,
+ 20.8823,
+ 21.114325,
+ 21.346351,
+ 21.578376,
+ 21.810402,
+ 22.042427,
+ 22.274453,
+ 22.506478,
+ 22.738504,
+ 22.970529,
+ 23.202555,
+ 23.434581,
+ 23.666606,
+ 23.898632,
+ 24.130657,
+ 24.362683,
+ 24.594708,
+ 24.826734,
+ 25.058759,
+ 25.290785,
+ 25.522811,
+ 25.754836,
+ 25.986862,
+ 26.218887,
+ 26.450913,
+ 26.682938,
+ 26.914964,
+ 27.146989,
+ 27.379015,
+ 27.61104,
+ 27.843066,
+ 28.075092,
+ 28.307117,
+ 28.539143,
+ 28.771168,
+ 29.003194,
+ 29.235219,
+ 29.467245,
+ 29.69927,
+ 29.931296,
+ 30.163322,
+ 30.395347,
+ 30.627373,
+ 30.859398,
+ 31.091424,
+ 31.323449,
+ 31.555475,
+ 31.7875,
+ 32.019526,
+ 32.251552,
+ 32.483577,
+ 32.715603,
+ 32.947628,
+ 33.179654,
+ 33.411679,
+ 33.643705,
+ 33.87573,
+ 34.107756,
+ 34.339781,
+ 34.571807,
+ 34.803833,
+ 35.035858,
+ 35.267884,
+ 35.499909,
+ 35.731935,
+ 35.96396,
+ 36.195986,
+ 36.428011,
+ 36.660037,
+ 36.892063,
+ 37.124088,
+ 37.356114,
+ 37.588139,
+ 37.820165,
+ 38.05219,
+ 38.284216,
+ 38.516241,
+ 38.748267,
+ 38.980292,
+ 39.212318,
+ 39.444344,
+ 39.676369,
+ 39.908395,
+ 40.14042,
+ 40.372446,
+ 40.604471,
+ 40.836497,
+ 41.068522,
+ 41.300548,
+ 41.532574,
+ 41.764599,
+ 41.996625,
+ 42.22865,
+ 42.460676,
+ 42.692701,
+ 42.924727,
+ 43.156752,
+ 43.388778,
+ 43.620803,
+ 43.852829,
+ 44.084855,
+ 44.31688,
+ 44.548906,
+ 44.780931,
+ 45.012957,
+ 45.244982,
+ 45.477008,
+ 45.709033,
+ 45.941059,
+ 46.173085,
+ 46.40511
+ ],
+ "relativeEmissivity": [
+ 1,
+ 0.995912,
+ 0.983808,
+ 0.964158,
+ 0.937698,
+ 0.905366,
+ 0.868236,
+ 0.827436,
+ 0.784084,
+ 0.73923,
+ 0.693814,
+ 0.648643,
+ 0.604381,
+ 0.561547,
+ 0.520527,
+ 0.481589,
+ 0.444898,
+ 0.410537,
+ 0.378521,
+ 0.348816,
+ 0.321349,
+ 0.296021,
+ 0.272717,
+ 0.251313,
+ 0.231678,
+ 0.213683,
+ 0.197201,
+ 0.182111,
+ 0.168296,
+ 0.155649,
+ 0.144069,
+ 0.133462,
+ 0.123741,
+ 0.114828,
+ 0.106651,
+ 0.099143,
+ 0.092246,
+ 0.085903,
+ 0.080067,
+ 0.074691,
+ 0.069736,
+ 0.065164,
+ 0.060942,
+ 0.05704,
+ 0.053431,
+ 0.050089,
+ 0.046992,
+ 0.044119,
+ 0.041452,
+ 0.038974,
+ 0.03667,
+ 0.034526,
+ 0.032528,
+ 0.030666,
+ 0.028929,
+ 0.027307,
+ 0.025791,
+ 0.024373,
+ 0.023047,
+ 0.021804,
+ 0.02064,
+ 0.019548,
+ 0.018523,
+ 0.01756,
+ 0.016656,
+ 0.015805,
+ 0.015005,
+ 0.014251,
+ 0.013541,
+ 0.012872,
+ 0.01224,
+ 0.011644,
+ 0.011082,
+ 0.01055,
+ 0.010047,
+ 0.009572,
+ 0.009122,
+ 0.008696,
+ 0.008293,
+ 0.007911,
+ 0.007548,
+ 0.007204,
+ 0.006878,
+ 0.006568,
+ 0.006274,
+ 0.005994,
+ 0.005728,
+ 0.005476,
+ 0.005235,
+ 0.005006,
+ 0.004788,
+ 0.004581,
+ 0.004383,
+ 0.004195,
+ 0.004015,
+ 0.003844,
+ 0.00368,
+ 0.003524,
+ 0.003375,
+ 0.003233,
+ 0.003097,
+ 0.002967,
+ 0.002843,
+ 0.002724,
+ 0.002611,
+ 0.002502,
+ 0.002399,
+ 0.002299,
+ 0.002204,
+ 0.002113,
+ 0.002026,
+ 0.001942,
+ 0.001862,
+ 0.001786,
+ 0.001712,
+ 0.001642,
+ 0.001574,
+ 0.001509,
+ 0.001447,
+ 0.001387,
+ 0.00133,
+ 0.001275,
+ 0.001222,
+ 0.001172,
+ 0.001123,
+ 0.001077,
+ 0.001032,
+ 0.000989,
+ 0.000947,
+ 0.000908,
+ 0.000869,
+ 0.000833,
+ 0.000797,
+ 0.000763,
+ 0.000731,
+ 0.000699,
+ 0.000669,
+ 0.00064,
+ 0.000612,
+ 0.000586,
+ 0.00056,
+ 0.000535,
+ 0.000511,
+ 0.000488,
+ 0.000466,
+ 0.000445,
+ 0.000425,
+ 0.000405,
+ 0.000386,
+ 0.000368,
+ 0.000351,
+ 0.000334,
+ 0.000318,
+ 0.000302,
+ 0.000287,
+ 0.000273,
+ 0.000259,
+ 0.000246,
+ 0.000233,
+ 0.000221,
+ 0.000209,
+ 0.000198,
+ 0.000187,
+ 0.000177,
+ 0.000167,
+ 0.000157,
+ 0.000148,
+ 0.000139,
+ 0.00013,
+ 0.000122,
+ 0.000115,
+ 0.000107,
+ 0.0001,
+ 9.3e-05,
+ 8.7e-05,
+ 8e-05,
+ 7.4e-05,
+ 6.9e-05,
+ 6.3e-05,
+ 5.8e-05,
+ 5.3e-05,
+ 4.8e-05,
+ 4.4e-05,
+ 4e-05,
+ 3.6e-05,
+ 3.2e-05,
+ 2.8e-05,
+ 2.5e-05,
+ 2.2e-05,
+ 1.9e-05,
+ 1.6e-05,
+ 1.4e-05,
+ 1.1e-05,
+ 9e-06,
+ 7e-06,
+ 5e-06,
+ 4e-06,
+ 2e-06,
+ 1e-06,
+ 0.0,
+ 0
+ ]
+}
\ No newline at end of file
diff --git a/labs/nebula/models/omega-centauri/mge-method.md b/labs/nebula/models/omega-centauri/mge-method.md
new file mode 100644
index 0000000000..a69ce3f711
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/mge-method.md
@@ -0,0 +1,80 @@
+# Omega Centauri MGE prior
+
+[photometric-mge.json](photometric-mge.json) transcribes the eight projected Gaussian
+components from [D’Souza & Rix (2013), Table 1](https://wwwmpa.mpa-garching.mpg.de/mpa/publications/preprints/pp2013/MPA3537.pdf).
+The recipe pins both the complete source PDF bytes and [physical-evidence.json](physical-evidence.json).
+The numerical extract is retained; the PDF remains a downloadable source, not a
+committed artifact. [Shape evidence](shape-evidence.md) explains the selected and
+competing measurements.
+
+## Transcription and units
+
+- Preserve the tabulated central amplitudes and projected axis ratios exactly.
+ Multiply every major-axis sigma in arcmin by60 to obtain `sigmaArcsec`.
+- The table amplitudes are central projected surface brightness, confirmed by
+ [Watkins et al. (2013), Table 1](https://academic.oup.com/mnras/article/436/3/2598/1258888).
+ Do not apply another1/q correction. The sampler divides all amplitudes by their
+ sum; their relative weights remain fixed.
+- Use angular coordinates in arcseconds throughout the depth sampler. For physical
+ interpretation, multiply by `distancePc × π/(180×3600)` to obtain parsecs. The
+ recipe's5426±47pc distance changes scale, not the observed angular shape or the
+ original paper's dynamical fit, which assumed5500±200pc.
+- Use the observation recipe's ICRS center for every lens and the prior. This explicitly
+ re-centers the older radial model; it is not a new center measurement.
+
+## Oblate deprojection
+
+Let `q′` denote the projected minor/major ratio, `i` the inclination (0° face-on),
+`σ` the major-axis width and `Σ₀` its relative projected central amplitude. Each
+axisymmetric Gaussian has:
+
+```text
+q² = (q′² − cos² i) / sin² i
+ρ₀ = Σ₀ q′ / (√(2π) σ q)
+ρ(R, z) = ρ₀ exp[−(R² + z²/q²) / (2σ²)]
+```
+
+Sum the eight components. Reject any inclination for which `q′² ≤ cos² i`;
+never clamp an impossible intrinsic ratio. The most flattened component requires
+`i > 43.603810°`. The selected50° follows the published model convention.
+
+The sky frame is **x west, y north, z away from Earth**. With major-axis PA measured
+east of north and authored tilt sign `s=+1`, the unit symmetry axis is:
+
+```text
+a = (sin i cos PA, sin i sin PA, s cos i)
+zIntrinsic = dot(a, position)
+R² = dot(position, position) − zIntrinsic²
+```
+
+The projected major axis is `(-sin PA, cos PA, 0)`. Reversing the line-of-sight tilt
+produces the same projected ellipse; the image does not identify which pole is
+nearer. The recipe records that choice instead of presenting it as a measurement.
+
+## Conditional image light and finite support
+
+The shared sampler truncates each component at ellipsoidal radius6σ. This authored
+numerical bound is not a measured tidal radius. Geometry and relative density are
+common to all image lenses. Preserve the images' integrated starlight and attach
+image material to finite 3D emitters conditioned on this prior.
+
+The current candidate separates a smooth envelope from positive image residuals.
+The envelope uses the MGE density at every depth, with a slowly varying gain fitted
+at an 8-pixel Gaussian smoothing scale and authored fraction 0.95. Its floor is zero;
+512 depth samples retain the central 99.6% of the image-weighted profile. Coarse
+smoothed lens chromaticity colors only this envelope. Up to 4096 residual features
+receive one conditional depth each and retain finite XYZ material; no full source
+photograph is repeated through depth. These numerical settings are authored display
+choices, not additional measurements of the cluster.
+
+The unchanged 512-pixel fit grid and minimum projected sigma of 0.9 fit pixels cannot
+recover native stellar widths or all crowded core texture. Compact replay retains
+the density, gain grid, coarse lens colors and finite supports explicitly. Actual
+bake and byte-identical replay acceptance remain separate from analytic fit checks.
+
+The result remains relative display emission under a smooth axisymmetric hypothesis.
+It does not supply measured member-star depths, population-specific mass segregation,
+a calibrated luminosity field, an N-body simulation or a solution to central
+counter-rotation. Source JSON and hash checks establish transcription and identity;
+registration, actual baking, side-view inspection and promotion require separate
+recorded results.
diff --git a/labs/nebula/models/omega-centauri/native-registration.json b/labs/nebula/models/omega-centauri/native-registration.json
new file mode 100644
index 0000000000..3d1eadf816
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/native-registration.json
@@ -0,0 +1,141 @@
+{
+ "schema": "cssearth-nebula-intake-registration-evidence@1",
+ "objectId": "omega-centauri",
+ "checkedAt": "2026-09-20",
+ "recipeSha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2",
+ "implementationSha256": "bcd06a8e7432a0998cd97d6b52af5876a2edc06d5824ba9f6421fb1331cd799e",
+ "method": "Exact native AVM WCS; automatic reciprocal field-star constellations, unchanged RANSAC affine and spatial holdout gates. Full native source pixels, bounded 8000-pixel detection only.",
+ "source": {
+ "id": "eso0844a",
+ "sha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429",
+ "width": 8040,
+ "height": 7560,
+ "avmSha256": "a440afd39241f5b40cb280e1a3ab511769265a55f8679baa75613f553940e1a5",
+ "wcs": {
+ "projection": "TAN",
+ "coordinateFrame": "ICRS",
+ "referenceDimension": [
+ 1280,
+ 1204
+ ],
+ "referencePixel": [
+ 620.447024342,
+ 636.048276073
+ ],
+ "referenceValueDeg": [
+ 201.697148,
+ -47.479684
+ ],
+ "scaleDeg": [
+ -0.00041507559,
+ 0.00041507559
+ ],
+ "rotationDeg": -0.00724264734
+ }
+ },
+ "reference": {
+ "id": "eso1119b",
+ "sha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "width": 14540,
+ "height": 14540,
+ "avmSha256": "6bcf0b5435aca887af71e8aff3107a654256fc4aaded598bb1db5419176cdeed",
+ "wcs": {
+ "projection": "TAN",
+ "coordinateFrame": "ICRS",
+ "referenceDimension": [
+ 1280,
+ 1280
+ ],
+ "referencePixel": [
+ 633.141898442,
+ 623.135269606
+ ],
+ "referenceValueDeg": [
+ 201.696944,
+ -47.479539
+ ],
+ "scaleDeg": [
+ -0.00066245096,
+ 0.00066245096
+ ],
+ "rotationDeg": 0.00051173061
+ }
+ },
+ "frame": {
+ "width": 1024,
+ "height": 1024,
+ "fieldArcminutes": [
+ 55,
+ 55
+ ],
+ "centerIcrsDegrees": [
+ 201.696958,
+ -47.479539
+ ],
+ "northUp": true
+ },
+ "matrix": [
+ 0.07383525828742865,
+ 0.000005393267322909706,
+ -0.0000035586654012105534,
+ 0.07383019788173614,
+ 224.53043293244812,
+ 248.99021978406566
+ ],
+ "pass": true,
+ "evidence": {
+ "status": "verified",
+ "matchedStars": 1899,
+ "trainingStars": 1266,
+ "trainingCandidates": 1266,
+ "rejectedTrainingStars": 0,
+ "heldOutStars": 633,
+ "rmsPixels": 0.030153681836255743,
+ "maxResidualPixels": 0.11949594972974975,
+ "spatialQuadrants": 4,
+ "coverageFraction": [
+ 0.9982448067846028,
+ 0.99705661691466
+ ],
+ "relativeScale": 1.000008976828227,
+ "centreShiftPixels": 0.4139475457539317,
+ "coverageDomain": {
+ "kind": "common-observed-footprint",
+ "sourcePixelPolygon": [
+ [
+ 8040.000000000001,
+ 0
+ ],
+ [
+ 8040,
+ 7560
+ ],
+ [
+ 0,
+ 7560
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ },
+ "residualArcseconds": 0.09717495123012104,
+ "interpretation": "Relative field-star alignment to the configured reference image; absolute sky scale and orientation remain publisher metadata. No stellar membership or physical depth."
+ },
+ "controls": {
+ "recentredPublisherReferencePixel": {
+ "pass": false,
+ "centreShiftPixels": 18.618235441877836
+ },
+ "corruptedHeldOutIdentity": {
+ "pass": false,
+ "maxResidualPixels": 3.013336415623728
+ }
+ },
+ "fullReceipt": {
+ "path": "output/omega-centauri-registration/native-registration-full.json",
+ "sha256": "b6287c7a5d548903a47f7ac1c8687d1d359f395214c6fdf8a42576b88b9f07aa"
+ },
+ "limits": "Relative alignment only; publisher absolute astrometry is not independently catalogue calibrated. WFI includes publisher-declared DSS gap fill. No depth, membership or photometric calibration is established."
+}
diff --git a/labs/nebula/models/omega-centauri/observation-structures.json b/labs/nebula/models/omega-centauri/observation-structures.json
new file mode 100644
index 0000000000..7e205539d1
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/observation-structures.json
@@ -0,0 +1,7 @@
+{
+ "schema": "cssearth-observation-structure-recipe@1",
+ "observationRecipe": "labs/nebula/models/omega-centauri/observations.json",
+ "observationCatalogue": ".local/nebula-lab/observations/omega-centauri/observations.json",
+ "workingWidth": 768,
+ "wavelets": { "scales": 6, "significanceSigma": 2, "compactMaxScale": 1, "elongatedAxisRatio": 2.5, "minRegionPixels": 5, "connectivity": 8 }
+}
diff --git a/labs/nebula/models/omega-centauri/observations.json b/labs/nebula/models/omega-centauri/observations.json
new file mode 100644
index 0000000000..9edd20d4f7
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/observations.json
@@ -0,0 +1,79 @@
+{
+ "schema": "cssearth-nebula-observation-recipe@1",
+ "id": "omega-centauri",
+ "referenceId": "eso1119b",
+ "frame": {
+ "width": 1024,
+ "height": 1024,
+ "fieldArcminutes": [55, 55],
+ "centerIcrsDegrees": [201.696958, -47.479539],
+ "northUp": true
+ },
+ "nativeRemoval": {
+ "model": {
+ "path": ".local/open-star-removal/noxGeneratorColor.pb",
+ "sha256": "d54bdca728d1d6db0b3eef41d4187d327909d1ec5cd2a71485bfa9d7924ba546"
+ },
+ "scriptSha256": "5db7c54230ba2032abb361cc2d59ef0e2e42114f27ef2036474c877058a6b752"
+ },
+ "images": [
+ {
+ "id": "eso1119b",
+ "label": "ESO VST/OmegaCAM · optical G/R/I",
+ "localCache": ".local/nebula-lab/source-originals/eso1119b.tif",
+ "url": "https://cdn.eso.org/images/original/eso1119b.tif",
+ "page": "https://www.eso.org/public/images/eso1119b/",
+ "sha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "width": 14540,
+ "height": 14540,
+ "fieldArcminutes": [50.88, 50.88],
+ "centerIcrsDegrees": [201.69695833333333, -47.47953888888889],
+ "northRightDegrees": -0.00051173061,
+ "bands": "Optical G (480nm) / R (625nm) / I (770nm)",
+ "credit": "ESO/INAF-VST/OmegaCAM. Acknowledgement: A. Grado, L. Limatola/INAF-Capodimonte Observatory",
+ "termsUrl": "https://www.eso.org/public/outreach/copyright/",
+ "registrationMode": "field-stars",
+ "registrationDetection": { "sourceMaximum": 8000, "referenceMaximum": 8000, "maximumStars": 12000 },
+ "stellarTreatment": "preserve",
+ "wcs": {
+ "projection": "TAN",
+ "coordinateFrame": "ICRS",
+ "referenceDimension": [1280, 1280],
+ "referencePixel": [633.141898442, 623.135269606],
+ "referenceValueDeg": [201.696944, -47.479539],
+ "scaleDeg": [-0.00066245096, 0.00066245096],
+ "rotationDeg": 0.00051173061
+ },
+ "metadataNote": "Exact embedded AVM WCS from the hash-pinned native TIFF, including its 1280-pixel reference grid and noncentral reference pixel. The AVM sky reference is not the raster centre. Native 14540x14540; the ignored derivative is an 8192px WebP. Relative alignment is tested against WFI; absolute astrometry remains publisher metadata."
+ },
+ {
+ "id": "eso0844a",
+ "label": "ESO 2.2m MPG/WFI · optical B/V/I",
+ "localCache": ".local/nebula-lab/source-originals/eso0844a.tif",
+ "url": "https://cdn.eso.org/images/original/eso0844a.tif",
+ "page": "https://www.eso.org/public/images/eso0844a/",
+ "sha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429",
+ "width": 8040,
+ "height": 7560,
+ "fieldArcminutes": [31.88, 29.99],
+ "centerIcrsDegrees": [201.69716666666667, -47.479683333333334],
+ "northRightDegrees": 0.00724264734,
+ "bands": "Optical B (451nm) / V (539nm) / I (783nm)",
+ "credit": "ESO",
+ "termsUrl": "https://www.eso.org/public/outreach/copyright/",
+ "registrationMode": "field-stars",
+ "registrationDetection": { "sourceMaximum": 8000, "referenceMaximum": 8000, "maximumStars": 12000 },
+ "stellarTreatment": "preserve",
+ "wcs": {
+ "projection": "TAN",
+ "coordinateFrame": "ICRS",
+ "referenceDimension": [1280, 1204],
+ "referencePixel": [620.447024342, 636.048276073],
+ "referenceValueDeg": [201.697148, -47.479684],
+ "scaleDeg": [-0.00041507559, 0.00041507559],
+ "rotationDeg": -0.00724264734
+ },
+ "metadataNote": "Exact embedded AVM WCS from the hash-pinned native TIFF, including its 1280x1204 reference grid and noncentral reference pixel. Independent telescope/instrument/epoch from VST/OmegaCAM. Publisher notes Digitized Sky Survey data fills some gaps in this mosaic; DSS-filled regions are not independently attributed star detections."
+ }
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/photometric-mge.json b/labs/nebula/models/omega-centauri/photometric-mge.json
new file mode 100644
index 0000000000..22a4e749c4
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/photometric-mge.json
@@ -0,0 +1,76 @@
+{
+ "schema": "cssearth-photometric-mge@1",
+ "id": "omega-centauri",
+ "centerIcrsDegrees": [
+ 201.696958,
+ -47.479539
+ ],
+ "distancePc": 5426,
+ "positionAngleEastOfNorthDegrees": 100,
+ "inclinationDegrees": 50,
+ "lineOfSightTiltSign": 1,
+ "cutoffSigma": 6,
+ "gaussians": [
+ {
+ "centralAmplitude": 1290.195,
+ "sigmaArcsec": 28.5342,
+ "projectedAxisRatio": 1
+ },
+ {
+ "centralAmplitude": 4662.587,
+ "sigmaArcsec": 115.88586,
+ "projectedAxisRatio": 0.9991714
+ },
+ {
+ "centralAmplitude": 2637.784,
+ "sigmaArcsec": 150.8031,
+ "projectedAxisRatio": 0.7799464
+ },
+ {
+ "centralAmplitude": 759.8591,
+ "sigmaArcsec": 212.20356,
+ "projectedAxisRatio": 0.724126
+ },
+ {
+ "centralAmplitude": 976.0853,
+ "sigmaArcsec": 324.22368,
+ "projectedAxisRatio": 0.8556435
+ },
+ {
+ "centralAmplitude": 195.4156,
+ "sigmaArcsec": 538.98336,
+ "projectedAxisRatio": 0.9392021
+ },
+ {
+ "centralAmplitude": 38.40327,
+ "sigmaArcsec": 836.175,
+ "projectedAxisRatio": 0.9555874
+ },
+ {
+ "centralAmplitude": 8.387379,
+ "sigmaArcsec": 1258.9254,
+ "projectedAxisRatio": 1
+ }
+ ],
+ "evidence": {
+ "path": "labs/nebula/models/omega-centauri/physical-evidence.json",
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51"
+ },
+ "source": {
+ "url": "https://wwwmpa.mpa-garching.mpg.de/mpa/publications/preprints/pp2013/MPA3537.pdf",
+ "sha256": "5701eed16944eb96e0d3e8dea258f909e99a1fa4a5a0305581e878909e70db92",
+ "locator": "D\u2019Souza & Rix2013, Table1, manuscript page12 (PDF page12), eight projected MGE components; arcmin dispersions multiplied by60."
+ },
+ "interpretation": "Published oblate MGE conditioned integrated starlight, interpreted as relative display light. Inclination50 degrees and PA100 degrees follow the selected literature model; the near/far tilt sign and six-sigma cutoff are authored. Gaussian widths retain their published angular scale at adopted distance5426pc and the common observation center. This is not measured star-by-star depth, calibrated luminosity, an N-body simulation or a completed bake/registration acceptance.",
+ "envelope": {
+ "scalePixels": 8,
+ "fraction": 0.95,
+ "floor": 0,
+ "depthSamples": 512,
+ "depthTrim": 0.002,
+ "chromaHalfSaturationQuantile": 0.5,
+ "chromaSkyQuantile": 0.1,
+ "chromaCoverageTaper": 0.5
+ },
+ "residualMaximumComponents": 4096
+}
diff --git a/labs/nebula/models/omega-centauri/physical-evidence.json b/labs/nebula/models/omega-centauri/physical-evidence.json
new file mode 100644
index 0000000000..b7cb1cb954
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/physical-evidence.json
@@ -0,0 +1,697 @@
+{
+ "schema": "cssearth-nebula-physical-evidence@1",
+ "subjectId": "omega-centauri",
+ "asOf": "2026-09-20",
+ "status": "Published photometric and dynamical constraints for a conditional oblate integrated-starlight model. The historical King approximation is rejected; runtime acceptance belongs to the delivered object evidence.",
+ "parameterClasses": {
+ "observed": "measured observable or metadata",
+ "published-model": "conditional literature inference, including a value we derived from measured inputs via a standard, non-object-specific mathematical relation",
+ "authored": "our transport, display, or approximation choice"
+ },
+ "coordinateRule": "Common tangent frame: x west, y north, z away from Earth; centered on the SIMBAD/publisher-agreed cluster center.",
+ "sources": [
+ {
+ "id": "baumgardt-hilker-2018",
+ "title": "A catalogue of masses, structural parameters, and velocity dispersion profiles of 112 Milky Way globular clusters",
+ "url": "https://doi.org/10.1093/mnras/sty1057",
+ "locator": "NGC 5139 row: core radius, half-mass radius, total mass, M/L, central velocity dispersion",
+ "access": "Cited from the task brief and cross-referenced against the author's public globular-cluster parameter tables (https://people.smp.uq.edu.au/HolgerBaumgardt/globular/); the paper PDF itself was not downloaded in this session, so its exact table values are not independently re-derived here."
+ },
+ {
+ "id": "baumgardt-2021-distances",
+ "title": "Baumgardt et al. (2021) globular cluster distances",
+ "url": "https://people.smp.uq.edu.au/HolgerBaumgardt/globular/",
+ "locator": "NGC 5139 distance entry",
+ "access": "Cited from the task brief; not independently re-fetched in this session."
+ },
+ {
+ "id": "simbad-ngc5139",
+ "title": "SIMBAD astronomical database, NGC 5139",
+ "url": "https://simbad.cds.unistra.fr/simbad/sim-id?Ident=NGC+5139",
+ "locator": "J2000 ICRS coordinates, reference 2010AJ....140.1830G",
+ "access": "Fetched live in this session; RA 13:26:47.28, Dec -47:28:46.1."
+ },
+ {
+ "id": "eso1119b-publisher",
+ "title": "ESO/INAF-VST OmegaCAM image eso1119b",
+ "url": "https://www.eso.org/public/images/eso1119b/",
+ "locator": "Field of view, pixel scale, pointing center, orientation",
+ "access": "Fetched live in this session; pointing center RA 13:26:47.27, Dec -47:28:46.34; 50.88 x 50.88 arcmin; north 0.0 degrees left of vertical; already acquired/hash-pinned per labs/nebula/sources/reference-images.json (id eso1119b)."
+ },
+ {
+ "id": "white-shawl-1987",
+ "title": "White & Shawl 1987, ellipticities of Galactic globular clusters",
+ "url": "https://ui.adsabs.harvard.edu/abs/1987ApJ...317..246W",
+ "locator": "Omega Centauri flattening measurement",
+ "access": "Cited from the task brief and corroborated by secondary literature search in this session; the paper PDF was not downloaded, and published ellipticity for Omega Cen varies with radius across the literature (roughly 0.08 in the core to ~0.17-0.19 in the outskirts)."
+ },
+ {
+ "id": "king-1962",
+ "title": "King 1962, The structure of star clusters I: an empirical density law",
+ "url": "https://ui.adsabs.harvard.edu/abs/1962AJ.....67..471K",
+ "locator": "Empirical projected surface-density formula used as the profile family",
+ "access": "Standard, widely reproduced closed-form formula; used from memory, not refetched."
+ },
+ {
+ "id": "wolf-2010",
+ "title": "Wolf et al. 2010, accurate masses for dispersion-supported galaxies",
+ "url": "https://doi.org/10.1111/j.1365-2966.2009.15708.x",
+ "locator": "Approximate relation between projected half-light radius and 3D half-mass radius for a broad family of density profiles",
+ "access": "Cited from general knowledge of the standard result, not refetched; used here only to fix one shape parameter, see evidence id projected-half-light-approx."
+ },
+ {
+ "id": "van-de-ven-2006-structure",
+ "title": "van de Ven, van den Bosch, Verolme & de Zeeuw (2006), The dynamical distance and intrinsic structure of the globular cluster omega Centauri",
+ "url": "https://doi.org/10.1051/0004-6361:20053061",
+ "locator": "A&A 445, 513-543; arXiv:astro-ph/0509228. Axisymmetric Schwarzschild orbit-superposition dynamical model combining line-of-sight velocities and internal proper motions.",
+ "access": "Full arXiv PDF downloaded and sections 2.3.3, 2.3.4 and 7.1 inspected on 2026-09-20; photometric major-axis PA100 degrees and coordinate convention confirmed.",
+ "download": {
+ "url": "https://arxiv.org/pdf/astro-ph/0509228",
+ "bytes": 1253405,
+ "sha256": "091115f8b04cf247f0d8ca04e7a6a5840fab8daa24f427168b6451fb37c13dd6"
+ }
+ },
+ {
+ "id": "reijns-2006-radial-velocities",
+ "title": "Reijns, Seitzer, Arnold, Freeman, Ingerson, van den Bosch, van de Ven & de Zeeuw (2006), Radial velocities in the globular cluster omega Centauri",
+ "url": "https://arxiv.org/abs/astro-ph/0509227",
+ "locator": "A&A 445, 503-511. 2756 radial velocities for 1966 stars (1589 members), ARGUS/CTIO 4m.",
+ "access": "Abstract fetched live in this session."
+ },
+ {
+ "id": "haberle-2024-omegacat2",
+ "title": "Haberle, Neumayer, Bellini, Libralato et al. (2024), oMEGACat II: Photometry and proper motions for 1.4 million stars in Omega Centauri and its rotation in the plane of the sky",
+ "url": "https://arxiv.org/html/2404.03722v2",
+ "locator": "Accepted v2, section 8.2: kinematic PA104 +/-1 degree and inclination43.9 +/-1.3 degrees.",
+ "access": "Full accepted-v2 HTML retrieved on 2026-09-20; section8.2 inspected.",
+ "download": {
+ "bytes": 740755,
+ "sha256": "9e46c2738830485c2f0eec0f7bde39654434a02a03a1d445010f2c37531b73c9"
+ }
+ },
+ {
+ "id": "bianchini-2013-flattening",
+ "title": "Bianchini, Varri, Bertin & Zocchi (2013), The flattening of globular clusters: internal rotation or velocity anisotropy?",
+ "url": "https://arxiv.org/abs/1302.0717",
+ "locator": "Axisymmetric rotating-anisotropic dynamical models applied to omega Centauri and 47 Tucanae.",
+ "access": "Abstract fetched live in this session."
+ },
+ {
+ "id": "pechetti-2024-muse-core",
+ "title": "Pechetti, Kamann, Krajnovic, Seth, van de Ven, Neumayer, Dreizler, Weilbacher, Martens & Wragg (2024), omega Centauri: a MUSE discovery of a counter-rotating core",
+ "url": "https://doi.org/10.1093/mnras/stae294",
+ "locator": "MNRAS 528, 4941-4957; arXiv:2401.15149.",
+ "access": "Abstract and title confirmed via live search in this session; not independently fetched in full."
+ },
+ {
+ "id": "trager-king-djorgovski-1995",
+ "title": "Trager, King & Djorgovski (1995), Catalogue of Galactic globular-cluster surface-brightness profiles",
+ "url": "https://ui.adsabs.harvard.edu/abs/1995AJ....109..218T",
+ "locator": "AJ 109, 218. Compiled surface-brightness profiles and King-model concentrations for 125 Milky Way globular clusters, including NGC 5139.",
+ "access": "Confirmed to exist and to cover this method via live search in this session; the specific NGC 5139 numeric table row (central surface brightness, concentration c, core radius) was not retrieved from the primary table itself."
+ },
+ {
+ "id": "mclaughlin-vandermarel-2005",
+ "title": "McLaughlin & van der Marel (2005), Resolved massive star clusters in the Milky Way and its satellites: brightness profiles and a catalogue of fundamental parameters",
+ "url": "https://arxiv.org/abs/astro-ph/0605132",
+ "locator": "CDS J/ApJS/161/304 Table10, NGC5139 rows229-231; Table11 derived structural radii.",
+ "access": "Complete CDS ReadMe, table10.dat and table11.dat downloaded on 2026-09-20. NGC5139 rows decoded using the archive byte-column definitions. Wilson and King(1966) fits use54 points; they are not the empirical King(1962) law.",
+ "tables": [
+ {
+ "url": "https://cdsarc.cds.unistra.fr/ftp/J/ApJS/161/304/ReadMe",
+ "bytes": 27936,
+ "sha256": "48b6e46ffeceb668e3cb108a1ffd8723df2382e0a07a6174260e199354e757b5"
+ },
+ {
+ "url": "https://cdsarc.cds.unistra.fr/ftp/J/ApJS/161/304/table10.dat",
+ "bytes": 75735,
+ "sha256": "536b829f310535759f572dd781f2c9d7cec602999d53f07c2bce9b0638f9abf1"
+ },
+ {
+ "url": "https://cdsarc.cds.unistra.fr/ftp/J/ApJS/161/304/table11.dat",
+ "bytes": 84456,
+ "sha256": "8707d3e19c864e058138bd7d0132ec06c97521a06ac4051132b3f7c149260a2d"
+ }
+ ]
+ },
+ {
+ "id": "noyola-gebhardt-bergmann-2008",
+ "title": "Noyola, Gebhardt & Bergmann (2008), Gemini and Hubble Space Telescope evidence for an intermediate-mass black hole in omega Centauri",
+ "url": "https://arxiv.org/abs/0801.2782",
+ "locator": "ApJ 676, 1008. HST ACS integrated-light surface-brightness profile of the cluster center.",
+ "access": "Confirmed via live search in this session; central cusp slope value taken from search summary of the paper, not from a direct fetch of the paper text."
+ },
+ {
+ "id": "baumgardt-vasiliev-2021-paper",
+ "title": "Baumgardt & Vasiliev (2021), Accurate distances to Galactic globular clusters through a combination of Gaia EDR3, HST, and literature data",
+ "url": "https://doi.org/10.1093/mnras/stab1474",
+ "locator": "Primary article distance table, NGC5139 row, combined-distance column:5.426 +0.047/-0.047 kpc.",
+ "access": "Primary article table inspected directly on 2026-09-20 at https://academic.oup.com/mnras/article/505/4/5957/6283731; exact distance and both uncertainty bounds confirmed."
+ },
+ {
+ "id": "merritt-1997",
+ "title": "Merritt, Meylan & Mayor (1997), The stellar dynamics of omega Centauri",
+ "url": "https://ui.adsabs.harvard.edu/abs/1997AJ....114.1074M",
+ "locator": "AJ 114, 1074. Early line-of-sight rotation-curve measurement.",
+ "access": "Confirmed via live search in this session; peak rotation value taken from search summary, not from a direct fetch of the paper text."
+ },
+ {
+ "id": "dsouza-rix-2013-mge",
+ "title": "D’Souza & Rix (2013), Mass estimates from stellar proper motions: the mass of omega Centauri",
+ "url": "https://doi.org/10.1093/mnras/sts426",
+ "locator": "Section4.3 and Table1, manuscript page12: eight Gaussian fit to Ferraro2006 number-density and Geyer1983 flattening profiles.",
+ "access": "Complete author-hosted PDF downloaded and table transcribed on2026-09-20; all eight rows cross-checked against Watkins2013 Table1. Coefficient error bars/covariance are not supplied in the table.",
+ "download": {
+ "url": "https://wwwmpa.mpa-garching.mpg.de/mpa/publications/preprints/pp2013/MPA3537.pdf",
+ "bytes": 480080,
+ "sha256": "5701eed16944eb96e0d3e8dea258f909e99a1fa4a5a0305581e878909e70db92"
+ }
+ },
+ {
+ "id": "watkins-2013-mge-reuse",
+ "title": "Watkins, van de Ven, den Brok & van den Bosch (2013), Discrete dynamical models of omega Centauri",
+ "url": "https://academic.oup.com/mnras/article/436/3/2598/1258888",
+ "locator": "Section4.1 and Table1; central brightness in solar luminosities/pc2, sigma along the major axis in arcmin, projected flattening.",
+ "access": "Primary article table inspected on2026-09-20. Exact same eight MGE rows; caption resolves amplitude semantics. PA-80 degrees equals100 degrees for the undirected major axis; their signed rotation coefficients are not used here."
+ }
+ ],
+ "evidence": [
+ {
+ "id": "distance",
+ "classification": "observed",
+ "sourceIds": [
+ "baumgardt-vasiliev-2021-paper"
+ ],
+ "quantity": "heliocentric distance",
+ "value": {
+ "distancePc": 5426,
+ "uncertaintyPc": 47
+ },
+ "unit": "pc",
+ "scope": "Whole cluster",
+ "limitations": "Adopted angular-to-physical scale from the combined Gaia EDR3, HST and literature distance. Earlier rounded5430 +/-50pc is retained in historical King evidence; changing distance does not refit the published MGE angular shape or establish a dynamical mass."
+ },
+ {
+ "id": "total-mass",
+ "classification": "published-model",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "total dynamical mass",
+ "value": {
+ "massSolar": 3940000.0
+ },
+ "unit": "solar masses",
+ "scope": "Whole cluster",
+ "limitations": "N-body/dynamical-model mass, not directly used by the light-emissivity profile in this recipe; recorded for completeness and for the mass-segregation limitation below."
+ },
+ {
+ "id": "half-mass-radius",
+ "classification": "published-model",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "3D half-mass radius",
+ "value": {
+ "radiusPc": 10.42
+ },
+ "unit": "pc",
+ "scope": "Whole cluster, dynamical model fit",
+ "limitations": "A 3D dynamical mass radius, not a projected light radius. The historical King-Abel conversion to a light radius is rejected for the selected MGE; mass segregation prevents treating the two as interchangeable."
+ },
+ {
+ "id": "core-radius",
+ "classification": "published-model",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "3D King core radius",
+ "value": {
+ "radiusPc": 4.54
+ },
+ "unit": "pc",
+ "scope": "Whole cluster, dynamical model fit",
+ "limitations": "Historical King-Abel experiment set this 3D dynamical core radius equal to a projected King radius. That substitution is rejected for the selected MGE recipe; this value is retained as historical context only."
+ },
+ {
+ "id": "mass-to-light",
+ "classification": "observed",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "V-band mass-to-light ratio",
+ "value": {
+ "mOverLvSolar": 2.8,
+ "uncertainty": 0.1
+ },
+ "unit": "Msun/Lsun (V)",
+ "scope": "Whole cluster",
+ "limitations": "Recorded for completeness; not used to scale the recipe's relative emissivity, which is unitless display light, not calibrated luminosity."
+ },
+ {
+ "id": "central-velocity-dispersion",
+ "classification": "observed",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "central velocity dispersion",
+ "value": {
+ "sigmaKmS": 18.1
+ },
+ "unit": "km/s",
+ "scope": "Cluster center",
+ "limitations": "Kinematic evidence, not used in this image/light recipe; recorded because it is part of the measured structural package and could support a future kinematic cross-check."
+ },
+ {
+ "id": "ellipticity",
+ "classification": "observed",
+ "sourceIds": [
+ "white-shawl-1987"
+ ],
+ "quantity": "projected flattening 1 - b/a",
+ "value": {
+ "ellipticityApprox": 0.17
+ },
+ "unit": "dimensionless",
+ "scope": "Whole cluster; varies with radius across the literature (roughly 0.08 core to ~0.19 outskirts)",
+ "limitations": "Historical summary, not a constant flattening consumed by the selected recipe. The MGE uses all eight published projected Gaussian axis ratios to represent radial variation; it does not assume spherical symmetry."
+ },
+ {
+ "id": "cluster-center-agreement",
+ "classification": "observed",
+ "sourceIds": [
+ "simbad-ngc5139",
+ "eso1119b-publisher"
+ ],
+ "quantity": "publisher pointing center vs. independent catalogue position",
+ "value": {
+ "simbadRaDec": [
+ "13:26:47.28",
+ "-47:28:46.1"
+ ],
+ "publisherRaDec": [
+ "13:26:47.27",
+ "-47:28:46.34"
+ ],
+ "separationArcsec": 0.24,
+ "nativePixelScaleArcsecPerPx": 0.21
+ },
+ "unit": "arcsec",
+ "scope": "Whole-image registration check",
+ "limitations": "The stated0.24arcsec separation is approximately1.14 native pixels at0.21arcsec/pixel, so it is a center sanity check, not sub-pixel evidence or an independent field-star registration. The MGE center is explicitly re-anchored to observations.json; native registration belongs to its separately measured transform."
+ },
+ {
+ "id": "king-profile-family",
+ "classification": "published-model",
+ "sourceIds": [
+ "king-1962"
+ ],
+ "quantity": "single-mass empirical projected surface-density law",
+ "value": {
+ "formula": "Sigma(R) proportional to [ (1+(R/rc)^2)^-1/2 - (1+(rt/rc)^2)^-1/2 ]^2 for R <= rt"
+ },
+ "unit": "n/a",
+ "scope": "Adopted profile family for the projected light distribution",
+ "limitations": "Historical King-Abel experiment only; not consumed by photometric-mge.json. Standard single-mass isotropic empirical law; Omega Centauri has multiple stellar populations and measurable rotation/flattening that a single-mass spherical King law does not capture.",
+ "selection": "rejected-historical"
+ },
+ {
+ "id": "projected-half-light-approx",
+ "classification": "published-model",
+ "sourceIds": [
+ "wolf-2010"
+ ],
+ "quantity": "projected half-light radius as a fraction of the 3D half-mass radius",
+ "value": {
+ "factor": 0.75
+ },
+ "unit": "dimensionless",
+ "scope": "Used once, to convert the measured 3D half-mass radius into a target projected half-light radius for solving the King concentration",
+ "limitations": "Historical King-Abel experiment only; not consumed by photometric-mge.json. This factor is an approximate, broadly-applicable result for centrally concentrated dispersion-supported systems, not a value measured for Omega Centauri specifically.",
+ "selection": "rejected-historical"
+ },
+ {
+ "id": "abel-inverted-emissivity",
+ "classification": "published-model",
+ "sourceIds": [
+ "king-1962",
+ "wolf-2010"
+ ],
+ "quantity": "spherically symmetric 3D relative emissivity vs. radius, derived by Abel-inverting the fitted King projected profile",
+ "value": {
+ "path": "labs/nebula/models/omega-centauri/king-abel-profile.json"
+ },
+ "unit": "relative (peak-normalized)",
+ "scope": "0 to the solved tidal radius (46.4 pc)",
+ "limitations": "Historical King-Abel experiment only; not consumed by photometric-mge.json. Deterministic consequence of assumed spherical symmetry plus the King profile family and its two fixed shape parameters (approximations A and B recorded in king-abel-profile.json). It is not a released, independently measured 3D point table; it is our own Abel-inversion computation from measured/published-model inputs. The single strongest physical limitation is sphericity itself: Omega Centauri's true ellipticity (~0.08-0.19, entry 'ellipticity') means the real 3D structure is oblate/triaxial, not spherical, and this profile cannot represent that flattening.",
+ "selection": "rejected-historical"
+ },
+ {
+ "id": "no-3d-baking-baseline",
+ "classification": "authored",
+ "sourceIds": [
+ "king-1962"
+ ],
+ "quantity": "historical implementation blocker for the original King-Abel experiment",
+ "value": {
+ "status": "historical-blocker"
+ },
+ "unit": "n/a",
+ "scope": "Lab tooling",
+ "limitations": "The original experiment had no external fixed-density simulation baseline. The selected analytic photometric MGE is a separate published-model prior; this historical entry does not establish that the new recipe has been baked, visually accepted or promoted."
+ },
+ {
+ "id": "inclination-van-de-ven-2006",
+ "classification": "published-model",
+ "sourceIds": [
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "inclination of the cluster's symmetry axis to the line of sight",
+ "value": {
+ "inclinationDeg": 50,
+ "uncertaintyDeg": 4,
+ "meanIntrinsicAxialRatio": 0.78,
+ "axialRatioUncertainty": 0.03
+ },
+ "unit": "degrees",
+ "scope": "Whole cluster; axisymmetric Schwarzschild orbit-superposition model fit to line-of-sight velocities plus internal proper motions",
+ "limitations": "Axisymmetric dynamical inference, not direct geometry. The source reports50 +/-4 degrees and dynamical distance4.8 +/-0.3kpc. D’Souza2013 subsequently adopts50 +/-3 degrees for its MGE analysis; that is the selected model convention, not an independently combined uncertainty. The recipe uses the newer observed distance only to set physical scale."
+ },
+ {
+ "id": "inclination-haberle-2024",
+ "classification": "published-model",
+ "sourceIds": [
+ "haberle-2024-omegacat2"
+ ],
+ "quantity": "inclination, independent modern check",
+ "value": {
+ "inclinationDeg": 43.9,
+ "uncertaintyDeg": 1.3
+ },
+ "unit": "degrees",
+ "scope": "Inner few arcminutes, from combined HST+MUSE proper motions (plane-of-sky rotation)",
+ "limitations": "Accepted-v2 axisymmetric kinematic inference, not direct depth. The1.3-degree interval crosses the selected MGE admissibility boundary43.603810 degrees; this newer fit cannot be substituted as an unconstrained inclination distribution into the older eight-Gaussian profile. Retained as a competing result, not erased or averaged with the selected50-degree convention."
+ },
+ {
+ "id": "rotation-amplitude-reijns-2006",
+ "classification": "observed",
+ "sourceIds": [
+ "reijns-2006-radial-velocities"
+ ],
+ "quantity": "line-of-sight rotation amplitude",
+ "value": {
+ "amplitudeKmS": 6,
+ "radiusRangeArcmin": [
+ 6,
+ 10
+ ]
+ },
+ "unit": "km/s",
+ "scope": "Radial-velocity survey, 1589 member stars",
+ "limitations": "Directly observed rotation signal (not a model output), but only along the line of sight; does not by itself give a 3D rotation axis or amplitude. Velocity dispersion drops from about 15 km/s in the inner few arcmin to about 6 km/s at 25 arcmin, so the rotation amplitude is a sizeable fraction of the local dispersion in the outer cluster."
+ },
+ {
+ "id": "rotation-amplitude-merritt-1997",
+ "classification": "observed",
+ "sourceIds": [
+ "merritt-1997"
+ ],
+ "quantity": "earlier line-of-sight rotation amplitude estimate",
+ "value": {
+ "amplitudeKmS": 7.9,
+ "radiusPc": 11
+ },
+ "unit": "km/s",
+ "scope": "Earlier radial-velocity dataset, independent of Reijns et al. (2006)",
+ "limitations": "Recorded from a search-engine summary of the paper, not a direct fetch of the paper text in this session, so treat the exact 7.9 km/s figure as approximate pending primary verification. Given alongside rotation-amplitude-reijns-2006 to show two independent studies agree the amplitude is of order 6-8 km/s, not to imply a precise consensus number."
+ },
+ {
+ "id": "rotation-flattening-relation",
+ "classification": "published-model",
+ "sourceIds": [
+ "bianchini-2013-flattening"
+ ],
+ "quantity": "whether rotation alone explains the photometric flattening",
+ "value": {
+ "conclusion": "not fully; a purely rotationally-flattened oblate isotropic model under-/over-predicts the observed ellipticity profile at some radii, and matching it needs added radial velocity anisotropy"
+ },
+ "unit": "n/a",
+ "scope": "Whole cluster, axisymmetric rotating-anisotropic dynamical models",
+ "limitations": "This is model-dependent evidence, not a raw measurement. Its direct consequence for this ledger: an oblate model built only from a measured ellipticity, position angle and inclination (isotropic King/Wilson made oblate) is known to be an approximation even under the axisymmetric assumption; velocity anisotropy, not just rotation, contributes to the real shape."
+ },
+ {
+ "id": "counter-rotating-core",
+ "classification": "observed",
+ "sourceIds": [
+ "pechetti-2024-muse-core"
+ ],
+ "quantity": "kinematically distinct core",
+ "value": {
+ "counterRotatesInner": true,
+ "innerRadiusArcsec": 20,
+ "innerRadiusPc": 0.5
+ },
+ "unit": "n/a",
+ "scope": "Innermost ~20 arcsec (~0.5 pc), MUSE narrow+wide field integral-field spectroscopy",
+ "limitations": "Direct observational evidence that the cluster's inner few tenths of a parsec rotate opposite to the bulk cluster rotation. This is incompatible with a single rigid-body oblate rotation pattern extended all the way to the center; any oblate King/Wilson model built from bulk ellipticity/PA/inclination necessarily misses this innermost feature, which occupies a negligible fraction of the cluster's volume but is a real, measured departure from simple oblate rotation."
+ },
+ {
+ "id": "kinematic-disk-component",
+ "classification": "published-model",
+ "sourceIds": [
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "separate disk-like kinematic substructure",
+ "value": {
+ "radialRangeArcmin": [
+ 1,
+ 3
+ ],
+ "massFraction": 0.04
+ },
+ "unit": "dimensionless (mass fraction)",
+ "scope": "Axisymmetric dynamical model, intermediate radii",
+ "limitations": "A model-inferred substructure (about 4% of total mass) in the same dynamical fit that gives the headline inclination number above; recorded because it is additional evidence that the cluster's true structure is not a single smooth oblate body, even though it is a small mass fraction and localized to a specific radial range."
+ },
+ {
+ "id": "king-wilson-fit-comparison",
+ "classification": "published-model",
+ "sourceIds": [
+ "mclaughlin-vandermarel-2005"
+ ],
+ "quantity": "comparison of published King(1966), Wilson(1975) and power-law fits",
+ "value": {
+ "pointCount": 54,
+ "unreducedChiSquared": {
+ "king1966": 89.81,
+ "wilson1975": 24.28,
+ "powerLaw": 76.25
+ },
+ "wilson": {
+ "centralPotentialW0": 4.7,
+ "centralPotentialUncertainty": 0.1,
+ "scaleRadiusArcsec": 196.81,
+ "scaleRadiusUncertaintyPlusArcsec": 7.08,
+ "scaleRadiusUncertaintyMinusArcsec": 7.01,
+ "concentrationLogRtOverR0": 1.34,
+ "concentrationUncertainty": 0.03,
+ "centralSurfaceBrightnessMagArcsec2": 16.52,
+ "centralSurfaceBrightnessUncertainty": 0.05,
+ "paperDistancePc": 5300
+ }
+ },
+ "unit": "n/a",
+ "scope": "Whole-cluster projected surface-brightness photometry",
+ "limitations": "Actual CDS rows now retrieved. Wilson is a better fit within this comparison, not proof that every King-family or other profile is impossible. r0 is a model scale radius, not the projected half-power core radius. Wilson requires its own distribution-function/Poisson solution; do not insert these parameters into King1962. The selected MGE already supplies a published radially varying flattening and an analytic deprojection."
+ },
+ {
+ "id": "central-cusp-noyola-2008",
+ "classification": "observed",
+ "sourceIds": [
+ "noyola-gebhardt-bergmann-2008"
+ ],
+ "quantity": "central surface-brightness slope",
+ "value": {
+ "logarithmicSlope": -0.08
+ },
+ "unit": "dimensionless (d log I / d log r)",
+ "scope": "Innermost several arcsec, HST ACS integrated light",
+ "limitations": "Shows a shallow rising cusp rather than a flat King core at the very center; part of the still-debated intermediate-mass-black-hole literature for this cluster (not adjudicated here), and irrelevant at the parsec scales the bulk King-Abel profile in this ledger targets, but recorded because it shows the innermost profile shape is not simply flat."
+ },
+ {
+ "id": "distance-confirmation-baumgardt-vasiliev-2021",
+ "classification": "observed",
+ "sourceIds": [
+ "baumgardt-vasiliev-2021-paper"
+ ],
+ "quantity": "adopted heliocentric distance from combined observations",
+ "value": {
+ "distancePc": 5426,
+ "uncertaintyPc": 47
+ },
+ "unit": "pc",
+ "scope": "Whole cluster, Gaia EDR3 + HST + literature combination",
+ "limitations": "Primary table now inspected directly. This replaces the earlier rounded5430 +/-50pc input for the MGE recipe, while preserving the original King-Abel artifact unchanged."
+ },
+ {
+ "id": "position-angle-not-sourced",
+ "classification": "authored",
+ "sourceIds": [
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "status note: position angle of the major axis on the sky",
+ "value": {
+ "status": "historical-gap-resolved",
+ "resolvedBy": "photometric-major-axis-position-angle"
+ },
+ "unit": "n/a",
+ "scope": "Whole cluster",
+ "limitations": "Initial search summaries did not establish PA. Full primary section2.3.3 now supplies photometric major-axis100 degrees; the separate104 +/-1 degree modern kinematic fit remains recorded. This preserved entry is not a current missing-source claim."
+ },
+ {
+ "id": "triaxiality-not-sourced",
+ "classification": "authored",
+ "sourceIds": [],
+ "quantity": "status note: dedicated triaxial (three-axis) shape measurement",
+ "value": {
+ "status": "not found in this session"
+ },
+ "unit": "n/a",
+ "scope": "Whole cluster",
+ "limitations": "No direct triaxial spatial fit was qualified for this recipe. Counter-rotation, kinematic substructure and differing fitted inclinations do not by themselves prove triaxiality. The oblate MGE is an explicit conditional approximation; it does not exclude more complex structure."
+ },
+ {
+ "id": "photometric-mge-fit",
+ "classification": "published-model",
+ "sourceIds": [
+ "dsouza-rix-2013-mge",
+ "watkins-2013-mge-reuse"
+ ],
+ "quantity": "eight-component projected Multi-Gaussian Expansion",
+ "value": {
+ "recipePath": "labs/nebula/models/omega-centauri/photometric-mge.json",
+ "gaussianCount": 8,
+ "sourceSigmaUnit": "arcmin",
+ "recipeSigmaUnit": "arcsec",
+ "sigmaConversionFactor": 60,
+ "paperDistancePc": 5500,
+ "paperDistanceUncertaintyPc": 200,
+ "coefficientUncertainty": null
+ },
+ "unit": "central amplitude:Lsun/pc2; angular major-axis sigma:arcmin; projected axis ratio:dimensionless",
+ "scope": "Smooth projected stellar number-density/light and radial flattening profiles fitted in the cited paper; not individual star locations.",
+ "limitations": "Central projected amplitudes are transcribed as published, with no additional1/q correction. Use only relative amplitudes for conditional integrated image light. No coefficient uncertainties or covariance are supplied, and no errors are fabricated. Neither an absolute emissivity calibration nor a modern dynamical refit is performed."
+ },
+ {
+ "id": "photometric-major-axis-position-angle",
+ "classification": "observed",
+ "sourceIds": [
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "photometric major-axis position angle east of north",
+ "value": {
+ "positionAngleDegrees": 100,
+ "uncertaintyDegrees": null,
+ "radialRangeArcmin": [
+ 5,
+ 15
+ ]
+ },
+ "unit": "degrees",
+ "scope": "Elliptical isophotes fitted to the smoothed DSS image at5-15arcmin.",
+ "limitations": "The cited section does not give a formal PA uncertainty. This is an undirected major axis modulo180 degrees, not a signed rotation axis or evidence that all radii have identical PA."
+ },
+ {
+ "id": "mge-adopted-inclination",
+ "classification": "published-model",
+ "sourceIds": [
+ "dsouza-rix-2013-mge",
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "adopted inclination for the published oblate MGE interpretation",
+ "value": {
+ "inclinationDegrees": 50,
+ "paperAdoptedUncertaintyDegrees": 3,
+ "inclinationDefinition": "zero face-on,90 edge-on",
+ "minimumAdmissibleInclinationDegrees": 43.60381018068668
+ },
+ "unit": "degrees",
+ "scope": "Selected smooth axisymmetric deprojection.",
+ "limitations": "D’Souza2013 adopts50 +/-3 degrees citing van de Ven2006, whose own reported interval is50 +/-4 degrees. These are model-dependent conventions, not a new precision estimate. The recipe uses the central50-degree value; all components require qProjected squared greater than cos(inclination) squared."
+ },
+ {
+ "id": "modern-kinematic-position-angle",
+ "classification": "published-model",
+ "sourceIds": [
+ "haberle-2024-omegacat2"
+ ],
+ "quantity": "modern fitted kinematic position angle",
+ "value": {
+ "positionAngleDegrees": 104,
+ "uncertaintyDegrees": 1
+ },
+ "unit": "degrees east of north",
+ "scope": "HST+MUSE rotation fit in accepted-v2 section8.2.",
+ "limitations": "The quoted1-degree uncertainty follows the fitting step size. A kinematic fit is not interchangeable with the older photometric-isophote orientation used by this recipe."
+ },
+ {
+ "id": "mge-observation-frame-center",
+ "classification": "authored",
+ "sourceIds": [
+ "eso1119b-publisher",
+ "dsouza-rix-2013-mge"
+ ],
+ "quantity": "adopted common tangent-frame center",
+ "value": {
+ "centerIcrsDegrees": [
+ 201.696958,
+ -47.479539
+ ],
+ "observationRecipe": "labs/nebula/models/omega-centauri/observations.json",
+ "paperKinematicCenterIcrsDegrees": [
+ 201.69065,
+ -47.47855
+ ]
+ },
+ "unit": "degrees ICRS",
+ "scope": "One common center for the two image lenses and the conditional spatial prior.",
+ "limitations": "The published radial shape is explicitly re-centered on the observation-frame center; it is not an assertion that the older paper used this same center. Center metadata does not replace independent image registration."
+ },
+ {
+ "id": "mge-oblate-deprojection",
+ "classification": "published-model",
+ "sourceIds": [
+ "dsouza-rix-2013-mge",
+ "watkins-2013-mge-reuse"
+ ],
+ "quantity": "analytic oblate deprojection of the published projected Gaussians",
+ "value": {
+ "methodPath": "labs/nebula/models/omega-centauri/mge-method.md",
+ "intrinsicAxisRatio": "sqrt((qProjected^2-cos(i)^2)/sin(i)^2)",
+ "centralRelativeDensity": "Sigma0*qProjected/(sqrt(2*pi)*sigma*qIntrinsic)",
+ "density": "sum rho0*exp(-(R^2+zIntrinsic^2/qIntrinsic^2)/(2*sigma^2))",
+ "frame": "x west,y north,z away from Earth"
+ },
+ "unit": "relative light per angular-depth unit in the sampler",
+ "scope": "Conditional continuous depth prior for finite integrated-light emitters.",
+ "limitations": "An analytic inference under axisymmetry, not a released XYZ catalogue or measured star-by-star depth. It does not recover population-specific mass segregation, central counter-rotation or triaxial structure. Angular widths stay fixed when physical distance is changed."
+ },
+ {
+ "id": "mge-authored-transport",
+ "classification": "authored",
+ "sourceIds": [
+ "dsouza-rix-2013-mge"
+ ],
+ "quantity": "finite-support and mirror choices for conditional image-light transport",
+ "value": {
+ "lineOfSightTiltSign": 1,
+ "cutoffSigma": 6,
+ "amplitudeNormalization": "divide all projected central amplitudes by their sum",
+ "stellarTreatment": "preserve integrated starlight"
+ },
+ "unit": "dimensionless",
+ "scope": "Offline conditional depth/material construction; common geometry for every lens.",
+ "limitations": "The projected image cannot choose the near/far tilt; sign+1 is authored. Six-sigma truncation is a numerical support bound, not a measured tidal edge. The published profile conditions finite3D image emitters; it does not identify individual member stars or justify copying the same image through every depth. Source registration, actual bake, side-view acceptance and app promotion require their own evidence."
+ }
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/registration.json b/labs/nebula/models/omega-centauri/registration.json
new file mode 100644
index 0000000000..3edf0d8d34
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/registration.json
@@ -0,0 +1,26 @@
+{
+ "schema": "cssearth-nebula-intake-registration-evidence@1",
+ "objectId": "omega-centauri",
+ "checkedAt": "2026-09-19",
+ "source": {
+ "id": "eso1119b",
+ "sha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "width": 14540,
+ "height": 14540,
+ "fieldArcminutes": [50.88, 50.88],
+ "nativeArcsecPerPixel": 0.2099
+ },
+ "method": "Whole-field publisher-WCS-vs-catalogue center check. No star-by-star astrometric fit was run in this session.",
+ "check": {
+ "publisherPointingCenterRaDec": ["13:26:47.27", "-47:28:46.34"],
+ "simbadCatalogueRaDec": ["13:26:47.28", "-47:28:46.1"],
+ "separationArcsec": 0.24,
+ "verdict": "Publisher WCS center agrees with the independently catalogued cluster center (NGC 5139, SIMBAD, ref 2010AJ....140.1830G) to within 0.24 arcsec, sub-pixel at the native 0.21 arcsec/px scale."
+ },
+ "orientation": "Publisher states north is 0.0 degrees left of vertical (north-up); east/west handedness not independently re-derived from pixel data in this session.",
+ "limitations": [
+ "This is a whole-image pointing check, not a matched-star homography fit against an external astrometric catalogue (e.g. Gaia); no per-star registration residuals were computed.",
+ "The field is dominated by the crowded cluster itself, so a compact-source cross-match against a stellar catalogue (rather than field background galaxies) would be the appropriate next step before any pixel-level star-position use.",
+ "Sufficient for the evidence-ledger and profile work in this delivery; not sufficient by itself to satisfy the skill's full registration gate (METHOD.md step 2) for a pixel-level bake."
+ ]
+}
diff --git a/labs/nebula/models/omega-centauri/shape-evidence.md b/labs/nebula/models/omega-centauri/shape-evidence.md
new file mode 100644
index 0000000000..5f48125132
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/shape-evidence.md
@@ -0,0 +1,54 @@
+# Omega Centauri: projected shape and conditional depth
+
+The selected prior is the published eight-component oblate Multi-Gaussian Expansion
+(MGE), recorded in [photometric-mge.json](photometric-mge.json). It describes a smooth
+stellar light distribution with changing flattening across radius. Its 3D depth is
+conditional on axisymmetry and inclination; individual stellar depths are not measured.
+The [physical ledger](physical-evidence.json) records values, access status and limits;
+[mge-method.md](mge-method.md) gives the conversion and coordinate conventions.
+
+## Selected constraints
+
+| Constraint | Adopted value | Source and limitation |
+| --- | --- | --- |
+| Projected profile | Eight Gaussian amplitudes, angular widths and projected axis ratios | [D’Souza & Rix 2013](https://doi.org/10.1093/mnras/sts426), Table 1; full author PDF inspected. Coefficient uncertainties/covariance are not supplied. |
+| Photometric major axis | 100° east of north | [van de Ven et al. 2006](https://arxiv.org/pdf/astro-ph/0509228), §2.3.3: nearly constant between 5–15 arcmin. No formal error bar supplied there. |
+| Inclination | 50° | D’Souza adopts 50±3°; the earlier van de Ven fit reports 50±4°. This is a selected model convention, not a combined uncertainty estimate. |
+| Distance | 5426±47 pc | [Baumgardt & Vasiliev 2021](https://doi.org/10.1093/mnras/stab1474), combined-distance table, NGC 5139 row, inspected directly. Angular widths remain unchanged. |
+| Center | ICRS (201.696958°, −47.479539°) | Re-anchor the published radial shape to the common observation frame; the older paper used a different kinematic center. |
+
+The full paper and table resolve the earlier position-angle and fitted-profile gaps.
+[Watkins et al. 2013](https://academic.oup.com/mnras/article/436/3/2598/1258888),
+Table 1, independently reproduces the same MGE and explicitly identifies its
+amplitudes as central projected surface brightness. Its −80° major axis is equivalent
+to 100°; the signed rotation parameters are unnecessary for this static light prior.
+
+## Competing constraints and historical trials
+
+- **Modern kinematics:** [Häberle et al. 2024, accepted v2](https://arxiv.org/html/2404.03722v2),
+ §8.2, gives PA104±1° and inclination43.9±1.3°. This is a different kinematic fit.
+ The old MGE requires inclination greater than43.603810°; the newer uncertainty
+ interval crosses that boundary. It cannot be inserted into this profile without
+ checking every component's deprojection.
+- **Wilson fit:** the actual [McLaughlin & van der Marel CDS table](https://cdsarc.cds.unistra.fr/ftp/J/ApJS/161/304/table10.dat)
+ gives W0=4.70±0.10 and scale radius196.810 +7.080/−7.010 arcsec. Its chi²24.28 is
+ lower than the King(1966) fit's89.81 for the same54 points. This comparison does not
+ invalidate every other family; MGE supplies the radial flattening needed here.
+ Wilson's scale radius is not its projected core radius, and its parameters cannot
+ be substituted into an empirical King(1962) law.
+- **Rejected King-Abel experiment:** [king-abel-profile.json](king-abel-profile.json)
+ remains unchanged as historical evidence. Its spherical shape, substitution of a
+ 3D dynamical core radius for a projected radius, and approximate conversion from
+ mass radius to light radius are not used by the selected MGE.
+- **Unresolved structure:** rotation, central counter-rotation and population-dependent
+ dynamics complicate a smooth oblate interpretation. Those observations do not by
+ themselves prove triaxiality; no measured triaxial model is supplied here.
+
+## What the source recipe establishes
+
+It establishes a reproducible, source-backed conditional depth prior. The near/far
+mirror choice and finite cutoff are authored. The images retain integrated starlight;
+relative image emission does not become calibrated luminosity, resolved member stars
+or a stellar mass map. A published shape does not establish image registration,
+completed baking, credible side views or application acceptance. Those outcomes need
+their own receipts and visual checks.
diff --git a/labs/nebula/models/omega-centauri/verify-against-dss2-fits.mts b/labs/nebula/models/omega-centauri/verify-against-dss2-fits.mts
new file mode 100644
index 0000000000..d303d1b600
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/verify-against-dss2-fits.mts
@@ -0,0 +1,86 @@
+/** Register each press image against a real-WCS DSS2 FITS cutout, with both sides detected at the
+ * same ~1"/px working scale (the coarser survey's native sampling) so detectStars finds the same
+ * population of stars on both sides. Reuses the shared algorithm only; no new matching logic. */
+import { readFile, writeFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { createHash } from 'node:crypto';
+import sharp from 'sharp';
+import { decodeFits } from '../../../../tools/nebula/application/fits.ts';
+import { detectStars, matchStars, verifyRegistration, publisherTransform, type SkyRaster, type SkyFrame } from '../../packages/reconstruction/src/registration/stellar.ts';
+
+const root = process.cwd();
+const sha256 = (b: Buffer) => createHash('sha256').update(b).digest('hex');
+const load = async (p: string, expected: string) => { const b = await readFile(resolve(root, p)); if (sha256(b) !== expected) throw new Error(`Pin differs: ${p}`); return b; };
+
+// DSS2 red-band FITS, exact-by-construction WCS: fov=0.91667deg=3300", width=3300 -> 1.0"/px.
+const dss2Path = '.local/nebula-lab/source-originals/omega-centauri-dss2.fits';
+const dss2Bytes = await readFile(resolve(root, dss2Path));
+console.log('DSS2 sha256', sha256(dss2Bytes), 'bytes', dss2Bytes.length);
+const fits = decodeFits(dss2Bytes);
+console.log('DSS2 FITS', fits.width, fits.height);
+let lo = Infinity, hi = -Infinity;
+const sorted = Float32Array.from(fits.values).sort();
+lo = sorted[Math.floor(sorted.length * 0.01)]!; hi = sorted[Math.floor(sorted.length * 0.999)]!;
+const gray = Buffer.alloc(fits.width * fits.height);
+for (let i = 0; i < fits.values.length; i++) gray[i] = Math.max(0, Math.min(255, Math.round((fits.values[i]! - lo) / (hi - lo) * 255)));
+const dss2Png = await sharp(gray, { raw: { width: fits.width, height: fits.height, channels: 1 } }).png().toBuffer();
+const dss2: SkyRaster = {
+ width: fits.width, height: fits.height, fieldArcminutes: [fits.width / 60, fits.height / 60], centerIcrsDegrees: [201.696958, -47.479539], northRightDegrees: 0,
+ wcs: { projection: 'TAN', coordinateFrame: 'ICRS', referenceDimension: [fits.width, fits.height], referencePixel: [fits.width / 2, fits.height / 2],
+ referenceValueDeg: [201.696958, -47.479539], scaleDeg: [-1 / 3600, 1 / 3600], rotationDeg: 0 } as any,
+};
+console.log('Detecting DSS2 stars at 1.0"/px...');
+const dss2Stars = await detectStars(dss2Png, [fits.width, fits.height], fits.width, 6000);
+console.log('DSS2 stars', dss2Stars.length);
+
+const candidates = [
+ { id: 'eso1119b', path: '.local/nebula-lab/source-originals/eso1119b.tif', sha256: '8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6',
+ width: 14540, height: 14540, fieldArcminutes: [50.88, 50.88] as [number, number], centerIcrsDegrees: [201.69695833333333, -47.47953888888889] as [number, number], northRightDegrees: 0.00051173061 },
+ { id: 'eso0844a', path: '.local/nebula-lab/source-originals/eso0844a.tif', sha256: '384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429',
+ width: 8040, height: 7560, fieldArcminutes: [31.88, 29.99] as [number, number], centerIcrsDegrees: [201.69716666666667, -47.479683333333334] as [number, number], northRightDegrees: -0.00724264734 },
+];
+
+// Matching-frame resolution is swept independently of detection resolution (both press images and
+// DSS2 are detected at the same ~1.0"/px scale above; only the discovery/verification tolerance,
+// which is fixed in frame pixels, changes with frame width).
+const FRAME_WIDTHS = [3600, 1800, 900, 450, 225];
+
+const report: { schema: string; pass: boolean; sources: unknown[] } = { schema: 'cssearth-image-alignment-report@1', pass: true, sources: [] };
+for (const c of candidates) {
+ console.log(`--- ${c.id} downsampled to ~1"/px ---`);
+ const bytes = await load(c.path, c.sha256);
+ const targetScale = 1; // arcsec/px, matching DSS2 native
+ const newWidth = Math.round(c.width * (c.fieldArcminutes[0] * 60 / c.width) / targetScale);
+ const resized = await sharp(bytes).resize({ width: newWidth }).removeAlpha().toColourspace('srgb').png().toBuffer({ resolveWithObject: true });
+ const { data: resizedInfo } = { data: resized.data };
+ const actualW = resized.info.width, actualH = resized.info.height;
+ const scaleXArcsec = c.fieldArcminutes[0] * 60 / actualW, scaleYArcsec = c.fieldArcminutes[1] * 60 / actualH;
+ console.log(c.id, 'downsampled to', actualW, 'x', actualH, 'arcsec/px', scaleXArcsec, scaleYArcsec);
+ const downsampled: SkyRaster = {
+ width: actualW, height: actualH, fieldArcminutes: c.fieldArcminutes, centerIcrsDegrees: c.centerIcrsDegrees, northRightDegrees: c.northRightDegrees,
+ wcs: { projection: 'TAN', coordinateFrame: 'ICRS', referenceDimension: [actualW, actualH], referencePixel: [actualW / 2, actualH / 2],
+ referenceValueDeg: c.centerIcrsDegrees, scaleDeg: [-scaleXArcsec / 3600, scaleYArcsec / 3600], rotationDeg: c.northRightDegrees } as any,
+ };
+ const stars = await detectStars(resized.data, [actualW, actualH], actualW, 6000);
+ console.log(c.id, 'stars', stars.length);
+ let outcome: { pass: boolean; matrix: unknown; evidence: Record; frameWidth: number } | undefined;
+ for (const width of FRAME_WIDTHS) {
+ const frame: SkyFrame = { width, height: width, fieldArcminutes: [60, 60], centerIcrsDegrees: [201.696958, -47.479539], northUp: true };
+ const dss2Init = publisherTransform(dss2, frame), init = publisherTransform(downsampled, frame);
+ const pairs = matchStars(stars, dss2Stars, init, dss2Init);
+ console.log(c.id, 'frameWidth', width, 'arcsecPerPx', 3600 / width, 'pairs', pairs.length);
+ if (pairs.length < 45) continue;
+ const result = verifyRegistration(pairs, downsampled, frame, init, { width: dss2.width, height: dss2.height, imageToFrame: dss2Init });
+ console.log(JSON.stringify({ id: c.id, frameWidth: width, pass: result.pass, rmsPixels: result.evidence.rmsPixels, maxResidualPixels: result.evidence.maxResidualPixels,
+ residualArcseconds: result.evidence.residualArcseconds, matchedStars: result.evidence.matchedStars, trainingStars: result.evidence.trainingStars,
+ heldOutStars: result.evidence.heldOutStars, coverageFraction: result.evidence.coverageFraction }, null, 2));
+ outcome = { pass: result.pass, matrix: result.matrix, evidence: result.evidence, frameWidth: width };
+ if (result.pass) break;
+ }
+ if (!outcome) { outcome = { pass: false, matrix: null, evidence: { status: 'publisher', interpretation: 'No frame width in the swept range reached 45 matched pairs.' }, frameWidth: -1 }; console.log(c.id, 'IMPASSE: never reached 45 matched pairs'); }
+ if (!outcome.pass) report.pass = false;
+ report.sources.push({ id: c.id, pass: outcome.pass, sourcePath: c.path, sourceSha256: c.sha256, downsampledTo: [actualW, actualH], frameWidth: outcome.frameWidth,
+ geometry: { kind: 'matched-star-homography-vs-dss2-fits', matrix: outcome.matrix }, evidence: outcome.evidence });
+}
+await writeFile(resolve(root, 'labs/nebula/models/omega-centauri/dss2-fits-registration-report.json'), JSON.stringify(report, null, 2) + '\n');
+console.log('OVERALL PASS', report.pass);
diff --git a/labs/nebula/models/omega-centauri/verify-against-dss2.mts b/labs/nebula/models/omega-centauri/verify-against-dss2.mts
new file mode 100644
index 0000000000..be27ce84ef
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/verify-against-dss2.mts
@@ -0,0 +1,79 @@
+/** Register each press-release Omega Centauri image against an exact-WCS hips2fits DSS2 cutout,
+ * separately. Bypasses prepare-observations.ts only because its dependency graph currently pulls
+ * in an unrelated JWST toolchain module that throws on import when bundled; reuses the exact
+ * shared detectStars/matchStars/verifyRegistration algorithm, invents no matching logic. */
+import { readFile, writeFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { createHash } from 'node:crypto';
+import { detectStars, matchStars, verifyRegistration, publisherTransform, type SkyRaster, type SkyFrame } from '../../packages/reconstruction/src/registration/stellar.ts';
+
+const root = process.cwd();
+const sha256 = (bytes: Buffer) => createHash('sha256').update(bytes).digest('hex');
+async function loadBytes(path: string, expected: string): Promise {
+ const bytes = await readFile(resolve(root, path));
+ const got = sha256(bytes);
+ if (got !== expected) throw new Error(`Pin differs for ${path}: expected ${expected}, got ${got}`);
+ return bytes;
+}
+
+// hips2fits reference: WCS exact by construction from the request parameters (fov=1.0deg, width=3600, ra/dec, rotation 0).
+const dss2: SkyRaster & { path: string; sha256: string } = {
+ path: '.local/nebula-lab/source-originals/omega-centauri-dss2.jpg', sha256: 'e42fb97ed35ac675f4143d77d7aaba096948a96fceaad2e200b1f279a1cb7638',
+ width: 3600, height: 3600, fieldArcminutes: [60, 60], centerIcrsDegrees: [201.696958, -47.479539], northRightDegrees: 0,
+ wcs: { projection: 'TAN', coordinateFrame: 'ICRS', referenceDimension: [3600, 3600], referencePixel: [1800, 1800],
+ referenceValueDeg: [201.696958, -47.479539], scaleDeg: [-1 / 3600, 1 / 3600], rotationDeg: 0 } as any,
+};
+
+const candidates: (SkyRaster & { id: string; path: string; sha256: string })[] = [
+ { id: 'eso1119b', path: '.local/nebula-lab/source-originals/eso1119b.tif', sha256: '8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6',
+ width: 14540, height: 14540, fieldArcminutes: [50.88, 50.88], centerIcrsDegrees: [201.69695833333333, -47.47953888888889], northRightDegrees: 0.00051173061,
+ wcs: { projection: 'TAN', coordinateFrame: 'ICRS', referenceDimension: [14540, 14540], referencePixel: [7270, 7270],
+ referenceValueDeg: [201.69695833333333, -47.47953888888889], scaleDeg: [-0.20995873452544705 / 3600, 0.20995873452544705 / 3600], rotationDeg: 0.00051173061 } as any },
+ { id: 'eso0844a', path: '.local/nebula-lab/source-originals/eso0844a.tif', sha256: '384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429',
+ width: 8040, height: 7560, fieldArcminutes: [31.88, 29.99], centerIcrsDegrees: [201.69716666666667, -47.479683333333334], northRightDegrees: -0.00724264734,
+ wcs: { projection: 'TAN', coordinateFrame: 'ICRS', referenceDimension: [8040, 7560], referencePixel: [4020, 3780],
+ referenceValueDeg: [201.69716666666667, -47.479683333333334], scaleDeg: [-0.23791044776119402 / 3600, 0.23801587301587301 / 3600], rotationDeg: -0.00724264734 } as any },
+];
+
+const frame: SkyFrame = { width: 8192, height: 8192, fieldArcminutes: [60, 60], centerIcrsDegrees: [201.696958, -47.479539], northUp: true };
+console.log('frame arcsec/px', 60 * 60 / frame.width);
+
+console.log('Reading DSS2 reference...');
+const dss2Bytes = await loadBytes(dss2.path, dss2.sha256);
+console.log('Detecting DSS2 stars...');
+const dss2Stars = await detectStars(dss2Bytes, [dss2.width, dss2.height], 3600, 8000);
+console.log(`DSS2 stars: ${dss2Stars.length}`);
+const dss2Init = publisherTransform(dss2, frame);
+
+const report: { schema: string; pass: boolean; catalogue: string; sources: unknown[] } = {
+ schema: 'cssearth-image-alignment-report@1', pass: true, catalogue: 'labs/nebula/models/omega-centauri/observations.json', sources: [],
+};
+
+for (const candidate of candidates) {
+ console.log(`--- ${candidate.id} vs DSS2 ---`);
+ const bytes = await loadBytes(candidate.path, candidate.sha256);
+ const stars = await detectStars(bytes, [candidate.width, candidate.height], 8000, 12000);
+ console.log(`${candidate.id} stars: ${stars.length}`);
+ const initial = publisherTransform(candidate, frame);
+ const pairs = matchStars(stars, dss2Stars, initial, dss2Init);
+ console.log(`${candidate.id} matched pairs (pre-verify): ${pairs.length}`);
+ let outcome: { pass: boolean; matrix: unknown; evidence: Record };
+ if (pairs.length < 45) {
+ outcome = { pass: false, matrix: initial, evidence: { status: 'publisher', matchedStars: pairs.length, interpretation: 'Fewer than 45 matched pairs against the DSS2 reference.' } };
+ console.log(`${candidate.id}: IMPASSE, only ${pairs.length} matched pairs (need 45).`);
+ } else {
+ const result = verifyRegistration(pairs, candidate, frame, initial, { width: dss2.width, height: dss2.height, imageToFrame: dss2Init });
+ outcome = { pass: result.pass, matrix: result.matrix, evidence: result.evidence };
+ console.log(JSON.stringify({ id: candidate.id, pass: result.pass, rmsPixels: result.evidence.rmsPixels, maxResidualPixels: result.evidence.maxResidualPixels,
+ residualArcseconds: result.evidence.residualArcseconds, matchedStars: result.evidence.matchedStars, trainingStars: result.evidence.trainingStars,
+ heldOutStars: result.evidence.heldOutStars, spatialQuadrants: result.evidence.spatialQuadrants, coverageFraction: result.evidence.coverageFraction }, null, 2));
+ }
+ if (!outcome.pass) report.pass = false;
+ report.sources.push({ id: candidate.id, pass: outcome.pass, sourcePath: candidate.path, sourceSha256: candidate.sha256,
+ geometry: { kind: 'matched-star-homography', registration: outcome.pass ? { referenceWcs: dss2.wcs, referenceWidthPx: dss2.width, referenceHeightPx: dss2.height, imageToReferenceMatrix: outcome.matrix } : null },
+ evidence: outcome.evidence });
+}
+
+await writeFile(resolve(root, 'labs/nebula/models/omega-centauri/dss2-registration-report.json'), JSON.stringify(report, null, 2) + '\n');
+console.log('Wrote dss2-registration-report.json');
+console.log('OVERALL PASS:', report.pass);
diff --git a/labs/nebula/models/omega-centauri/verify-registration.mts b/labs/nebula/models/omega-centauri/verify-registration.mts
new file mode 100644
index 0000000000..2b1d96f41d
--- /dev/null
+++ b/labs/nebula/models/omega-centauri/verify-registration.mts
@@ -0,0 +1,77 @@
+/** Replay native registration from the actual processing recipe and embedded TIFF AVM metadata. */
+import { readFile, writeFile, mkdir } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { createHash } from 'node:crypto';
+import assert from 'node:assert/strict';
+import sharp from 'sharp';
+import { readObservationRecipe, observationSourceFile, type ObservationSource } from '../../packages/lab/src/features/observations/recipe.ts';
+import { detectStars, matchStars, verifyRegistration, publisherTransform } from '../../packages/reconstruction/src/registration/stellar.ts';
+
+const root = process.cwd(), directory = 'labs/nebula/models/omega-centauri';
+const sha256 = (bytes: Uint8Array) => createHash('sha256').update(bytes).digest('hex');
+const recipeBytes = await readFile(resolve(root, directory, 'observations.json'));
+const recipe = readObservationRecipe(JSON.parse(recipeBytes.toString()));
+const reference = recipe.images.find(image => image.id === recipe.referenceId)!;
+const source = recipe.images.find(image => image.id === 'eso0844a')!;
+if (!source || reference.id !== 'eso1119b') throw new Error('Expected the two pinned ESO observations.');
+
+function avmPair(xml: string, key: string): [number, number] {
+ const section = xml.match(new RegExp(`([\\s\\S]*?)`));
+ if (!section) throw new Error(`Missing AVM ${key}.`);
+ const values = [...section[1]!.matchAll(/([^<]+)<\/rdf:li>/g)].map(match => Number(match[1]));
+ if (values.length !== 2 || !values.every(Number.isFinite)) throw new Error(`Invalid AVM ${key}.`);
+ return [values[0]!, values[1]!];
+}
+async function loadNative(image: ObservationSource) {
+ const path = resolve(root, '.local/nebula-lab/observations', recipe.id, 'sources', observationSourceFile(image));
+ const bytes = await readFile(path);
+ if (sha256(bytes) !== image.sha256) throw new Error(`${image.id}: source hash differs.`);
+ const metadata = await sharp(bytes).metadata(), xml = metadata.xmp?.toString();
+ if (!xml || metadata.width !== image.width || metadata.height !== image.height) throw new Error(`${image.id}: missing native AVM or dimensions differ.`);
+ const rotation = xml.match(/avm:Spatial.Rotation="([^"]+)"/);
+ if (!rotation || !Number.isFinite(Number(rotation[1])) || !xml.includes('avm:Spatial.CoordinateFrame="ICRS"') ||
+ !xml.includes('avm:Spatial.CoordsystemProjection="TAN"')) throw new Error(`${image.id}: unexpected native sky frame.`);
+ const wcs = { projection: 'TAN', coordinateFrame: 'ICRS', referenceDimension: avmPair(xml, 'ReferenceDimension'),
+ referencePixel: avmPair(xml, 'ReferencePixel'), referenceValueDeg: avmPair(xml, 'ReferenceValue'),
+ scaleDeg: avmPair(xml, 'Scale'), rotationDeg: Number(rotation[1]) };
+ assert.deepEqual(image.wcs, wcs, `${image.id}: recipe must retain exact embedded AVM, including noncentral reference pixel.`);
+ const settings = image.registrationDetection;
+ if (!settings) throw new Error('Explicit native registration detection settings required.');
+ console.log(`REGISTRATION_DETECT ${image.id}`);
+ const stars = await detectStars(bytes, [image.width, image.height], settings.sourceMaximum, settings.maximumStars);
+ return { stars, avmSha256: sha256(Buffer.from(xml)), wcs };
+}
+const referenceNative = await loadNative(reference), sourceNative = await loadNative(source);
+const referenceInitial = publisherTransform(reference, recipe.frame), sourceInitial = publisherTransform(source, recipe.frame);
+const footprint = { width: reference.width, height: reference.height, imageToFrame: referenceInitial };
+const pairs = matchStars(sourceNative.stars, referenceNative.stars, sourceInitial, referenceInitial);
+const result = verifyRegistration(pairs, source, recipe.frame, sourceInitial, footprint);
+assert.equal(result.pass, true, 'The unchanged native registration gate must pass.');
+const centred = structuredClone(source);
+assert.ok(centred.wcs);
+centred.wcs.referencePixel = [centred.wcs.referenceDimension[0] / 2, centred.wcs.referenceDimension[1] / 2];
+const centredResult = verifyRegistration(pairs, centred, recipe.frame, publisherTransform(centred, recipe.frame), footprint);
+assert.equal(centredResult.pass, false, 'Reinstating the lost AVM offset must fail.');
+const corrupted = structuredClone(pairs), heldOut = result.evidence.matches.find(match => match.heldOut)!;
+corrupted.find(pair => pair.source[0] === heldOut.source[0] && pair.source[1] === heldOut.source[1])!.frame[0] += 3;
+const corruptedResult = verifyRegistration(corrupted, source, recipe.frame, sourceInitial, footprint);
+assert.equal(corruptedResult.pass, false, 'A wrong held-out identity must fail.');
+const output = resolve(root, 'output/omega-centauri-registration');
+await mkdir(output, { recursive: true });
+const fullReceipt = Buffer.from(JSON.stringify({ recipeSha256: sha256(recipeBytes), result }, null, 2) + '\n');
+await writeFile(resolve(output, 'native-registration-full.json'), fullReceipt);
+const { matches: _matches, ...evidence } = result.evidence;
+const receipt = { schema: 'cssearth-nebula-intake-registration-evidence@1', objectId: recipe.id,
+ checkedAt: new Date().toISOString().slice(0, 10), recipeSha256: sha256(recipeBytes),
+ implementationSha256: sha256(await readFile(resolve(root, 'labs/nebula/packages/reconstruction/src/registration/stellar.ts'))),
+ method: 'Exact native AVM WCS; automatic reciprocal field-star constellations, unchanged RANSAC affine and spatial holdout gates. Full native source pixels, bounded 8000-pixel detection only.',
+ source: { id: source.id, sha256: source.sha256, width: source.width, height: source.height, avmSha256: sourceNative.avmSha256, wcs: sourceNative.wcs },
+ reference: { id: reference.id, sha256: reference.sha256, width: reference.width, height: reference.height, avmSha256: referenceNative.avmSha256, wcs: referenceNative.wcs },
+ frame: recipe.frame, matrix: result.matrix, pass: result.pass, evidence,
+ controls: { recentredPublisherReferencePixel: { pass: centredResult.pass, centreShiftPixels: centredResult.evidence.centreShiftPixels },
+ corruptedHeldOutIdentity: { pass: corruptedResult.pass, maxResidualPixels: corruptedResult.evidence.maxResidualPixels } },
+ fullReceipt: { path: 'output/omega-centauri-registration/native-registration-full.json', sha256: sha256(fullReceipt) },
+ limits: 'Relative alignment only; publisher absolute astrometry is not independently catalogue calibrated. WFI includes publisher-declared DSS gap fill. No depth, membership or photometric calibration is established.' };
+await writeFile(resolve(root, directory, 'native-registration.json'), JSON.stringify(receipt, null, 2) + '\n');
+console.log(JSON.stringify({ pass: result.pass, ...evidence }, null, 2));
+console.log('OMEGA_NATIVE_REGISTRATION_VERIFIED');
diff --git a/labs/nebula/packages/lab/browser/browser-difference-map.ts b/labs/nebula/packages/lab/browser/browser-difference-map.ts
index 7054475b4b..2b0aec998b 100644
--- a/labs/nebula/packages/lab/browser/browser-difference-map.ts
+++ b/labs/nebula/packages/lab/browser/browser-difference-map.ts
@@ -66,9 +66,9 @@ try {
document.querySelector('.reconstruction-controls')?.getAttribute('data-reconstruction-result') === value, id, { timeout: 120000 });
await page.locator('#reference-view').click(); await settle(page);
- // The second round button, directly under Levels.
+ // The second of three round buttons, directly under Levels (Radial profile sits third, under Difference map).
const order = await page.locator('.workspace-tool-buttons [data-workspace-tool]').evaluateAll(nodes => nodes.map(node => (node as HTMLElement).dataset.workspaceTool));
- assert.deepEqual(order, ['levels', 'difference'], `tool order ${order.join(',')}`);
+ assert.deepEqual(order, ['levels', 'difference', 'radial'], `tool order ${order.join(',')}`);
const [levelsBox, differenceBox] = await Promise.all(['levels', 'difference'].map(tool => page.locator(`[data-workspace-tool="${tool}"]`).boundingBox()));
assert.ok(levelsBox && differenceBox && differenceBox.y > levelsBox.y + levelsBox.height - 1 && Math.abs(differenceBox.x - levelsBox.x) < .5, 'not under Levels');
diff --git a/labs/nebula/packages/lab/browser/browser-lens-radial.ts b/labs/nebula/packages/lab/browser/browser-lens-radial.ts
new file mode 100644
index 0000000000..5428e06168
--- /dev/null
+++ b/labs/nebula/packages/lab/browser/browser-lens-radial.ts
@@ -0,0 +1,69 @@
+/**
+ * The live Radial profile panel in the real Reconstruction sidebar, for two image lenses.
+ *
+ * Read-only: it selects lenses through the page's own image picker and opens the panel, so the measurement
+ * arrives through the same route a person browsing the lab hits. Args: [base-url] [output-directory].
+ */
+import assert from 'node:assert/strict';
+import { mkdir, writeFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { chromium, type Page } from 'playwright';
+
+const base = process.argv[2] ?? 'http://127.0.0.1:4331';
+const output = resolve(process.argv[3] ?? '.local/nebula-lab/lens-radial');
+const lenses = ['horalek-widefield', 'vista-infrared'];
+await mkdir(output, { recursive: true });
+
+const errors: string[] = [], ignored: string[] = [], report: unknown[] = [];
+const browser = await chromium.launch({ headless: true });
+try {
+ const page: Page = await browser.newPage({ viewport: { width: 1500, height: 1050 } });
+ const note = (text: string) => { (/favicon/.test(text) ? ignored : errors).push(text); };
+ page.on('pageerror', error => note(String(error)));
+ page.on('console', message => { if (message.type() === 'error') note(`${message.text()} @ ${message.location().url || 'unknown'}`); });
+ page.on('response', response => { if (response.status() >= 400) note(`http ${response.status()} ${response.url()}`); });
+
+ const catalogue = await (await fetch(`${base}/__nebula/reconstruction?subjectId=lmc-clouds`)).json();
+ assert.ok(catalogue.finiteModel?.modelResultId, 'The LMC needs a discovered finite model to have lenses.');
+ await page.goto(`${base}/reconstruction?subject=lmc-clouds`);
+ await page.waitForSelector('#reconstruction-image option[value="horalek-widefield"]', { state: 'attached' });
+ for (const imageId of lenses) {
+ const expected = catalogue.candidates.find((row: { imageId: string }) => row.imageId === imageId);
+ assert.ok(expected?.prepared?.resultId, `No baked lens for ${imageId}.`);
+ await page.selectOption('#reconstruction-image', imageId);
+ await page.waitForFunction(id => document.querySelector('.reconstruction-controls')?.getAttribute('data-reconstruction-result') === id,
+ expected.prepared.resultId, { timeout: 120000 });
+
+ // The third round button, under Levels and Difference map.
+ const order = await page.locator('.workspace-tool-buttons [data-workspace-tool]').evaluateAll(nodes => nodes.map(node => (node as HTMLElement).dataset.workspaceTool));
+ assert.deepEqual(order, ['levels', 'difference', 'radial'], `tool order ${order.join(',')}`);
+ await page.locator('[data-workspace-tool="radial"]').click();
+ const panel = page.locator('.lens-radial-panel');
+ await panel.waitFor();
+ await page.waitForFunction(id => {
+ const node = document.querySelector('.lens-radial-panel');
+ return node?.dataset.lensRadialState === 'ready' && node.dataset.lensRadialResult === id;
+ }, expected.prepared.resultId, { timeout: 120000 });
+ const measured = await panel.evaluate(node => ({
+ resultId: (node as HTMLElement).dataset.lensRadialResult,
+ polylines: node.querySelectorAll('polyline').length,
+ canvas: node.querySelectorAll('canvas').length,
+ numbers: node.querySelector('.lens-radial-numbers')?.textContent?.trim() ?? '',
+ status: node.querySelector('.lens-radial-status')?.textContent?.trim() ?? '',
+ }));
+ assert.equal(measured.resultId, expected.prepared.resultId);
+ // Source, render and ratio strokes, drawn as SVG; never a canvas.
+ assert.equal(measured.polylines, 3, `${imageId} polylines ${measured.polylines}`);
+ assert.equal(measured.canvas, 0);
+ assert.match(measured.numbers, /half-light radius .+px.+rms log ratio .+worst/s);
+ assert.match(measured.status, /grid \d+×\d+ · footprint [\d,]+ px/);
+ await panel.screenshot({ path: `${output}/radial-${imageId}.png` });
+ await page.screenshot({ path: `${output}/lab-${imageId}.png` });
+ report.push({ imageId, ...measured });
+ // Close the panel so the next lens starts from the same closed state.
+ await page.locator('.workspace-tool-close').click();
+ }
+ assert.deepEqual(errors, [], `Console errors: ${errors.join(' | ')}`);
+} finally { await browser.close(); }
+await writeFile(`${output}/report.json`, JSON.stringify({ base, lenses, ignored, report }, null, 2) + '\n');
+console.log('LENS_RADIAL_BROWSER_OK', output, JSON.stringify(report.map((row: any) => [row.imageId, row.polylines])));
diff --git a/labs/nebula/packages/lab/package.json b/labs/nebula/packages/lab/package.json
index d2b59764a2..8953e057e6 100644
--- a/labs/nebula/packages/lab/package.json
+++ b/labs/nebula/packages/lab/package.json
@@ -42,6 +42,7 @@
"@layoutit/polycss": "0.2.11",
"@cssearth/engine": "workspace:*",
"@cssearth/objects": "workspace:*",
+ "typescript": "5.9.3",
"vite": "7.3.1"
},
"devDependencies": {
diff --git a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler-export.ts b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler-export.ts
index c6a125a94a..bbc3c1667a 100644
--- a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler-export.ts
+++ b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler-export.ts
@@ -21,7 +21,7 @@ export async function exportCompactCompiler(root: string, objectId: string) {
const material = receipts.find(m => record(m) && m.sourceId === lens.id);
if (!record(material) || material.schema !== 'cssearth-component-bound-material@1' || material.fieldIdentity !== result.scene.fieldIdentity || !Array.isArray(material.components))
throw new TypeError(`Missing accepted component colors for ${lens.id}.`);
- return { sourceId: lens.id, components: material.components.map((color: unknown) => {
+ return { sourceId: lens.id, ...(material.envelopeColors === undefined ? {} : { envelopeColors: material.envelopeColors }), components: material.components.map((color: unknown) => {
if (!record(color)) throw new TypeError('Invalid retained component.');
return { id: color.id, rgb: color.rgb, covered: color.covered };
}) };
@@ -36,7 +36,7 @@ export async function exportCompactCompiler(root: string, objectId: string) {
}
const input = { schema: 'cssearth-compact-compiler@1', objectId,
provenance: { resultPath, resultSha256: hash(resultBytes), modelSha256: hash(modelBytes), methodSha256: hash(methodBytes),
- interpretation: 'Accepted fitted emission components, per-component source chromaticity, stars and sampling. Derived field inputs, not measured volumetric gas density. Full source acquisition remains available in the research recipes.' },
+ interpretation: 'Accepted fitted emission components, any retained photometric envelope with coarse source chromaticity, per-component colors, stars and sampling. Derived field inputs, not measured volumetric gas density. Full source acquisition remains available in the research recipes.' },
field: JSON.parse(modelBytes.toString()), scene: result.scene, materials,
sources: result.scene.lenses.map(lens => { const source = result.sources.find(source => source.id === lens.id)!;
return { id: lens.id, label: lens.label, credit: source.credit, page: source.page }; }),
diff --git a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.test.ts b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.test.ts
index ca2e31cbce..95da8af0e5 100644
--- a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.test.ts
+++ b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.test.ts
@@ -26,3 +26,46 @@ test('compact compiler validates the accepted field, material correspondence and
shape.expected[0].resources[0].width = 0;
assert.throws(() => readCompactCompiler(shape), /resource/);
});
+
+import { createHash } from 'node:crypto';
+test('compact replay cannot silently omit a retained density envelope or its colors', async () => {
+ const value = await input();
+ const recipe = JSON.parse(await readFile('labs/nebula/models/omega-centauri/photometric-mge.json', 'utf8'));
+ value.field.photometricEnvelope = { schema: 'cssearth-photometric-envelope@1', priorIdentity: 'c'.repeat(64), recipe,
+ width: 2, height: 2, bounds: { min: [-100, -100], max: [100, 100] }, zRange: [-300, 300], gain: [1, 1, 1, 1] };
+ for (const material of value.materials) material.envelopeColors = { width: 2, height: 2, rgb: Array(12).fill(255) };
+ value.provenance.modelSha256 = createHash('sha256').update(JSON.stringify(value.field)).digest('hex');
+ assert.ok(readCompactCompiler(value).field.photometricEnvelope);
+ const missingColors = structuredClone(value); delete missingColors.materials[0].envelopeColors;
+ assert.throws(() => readCompactCompiler(missingColors), /envelope colors/);
+ const missingField = structuredClone(value); delete missingField.field.photometricEnvelope;
+ for (const material of missingField.materials) delete material.envelopeColors;
+ assert.throws(() => readCompactCompiler(missingField), /retained model hash/);
+});
+
+test('compact sampling keeps historical omission and rejects a grouped plan that differs from saved output counts', async () => {
+ const historical = await input(), before = JSON.stringify(historical.scene.sampling);
+ assert.equal(JSON.stringify(readCompactCompiler(historical).scene.sampling), before);
+ assert.equal('layerPlan' in readCompactCompiler(historical).scene.sampling, false);
+ const planned = structuredClone(historical), reference = { ...planned.scene.sampling.sliceCounts };
+ const groups = (count: number) => Array.from({ length: Math.ceil(count / 4) }, (_, i) => ({ startCell: i * 4, endCell: Math.min(count, i * 4 + 4) }));
+ const axes = { x: groups(reference.x), y: groups(reference.y), z: groups(reference.z) };
+ planned.scene.sampling.layerPlan = { schema: 'cssearth-volume-layer-plan@1', referenceSliceCounts: reference,
+ referenceSamplesPerSlab: 4, axes };
+ planned.scene.sampling.sliceCounts = { x: axes.x.length, y: axes.y.length, z: axes.z.length };
+ assert.deepEqual(readCompactCompiler(planned).scene.sampling, planned.scene.sampling);
+ const reported = structuredClone(planned);
+ reported.scene.sampling.layerOptimization = { schema: 'cssearth-layer-optimization@1',
+ method: 'gradient-weighted-depth-displacement@1', maximumLayers: 500,
+ referenceLayers: reference.x + reference.y + reference.z, plannedLayers: axes.x.length + axes.y.length + axes.z.length,
+ probeWidth: 64, probeSubpixels: 2, targetNormalizedL1: .01, estimatedNormalizedL1: { x: .005, y: .008, z: .009 }, status: 'target-met' };
+ assert.deepEqual(readCompactCompiler(reported).scene.sampling, reported.scene.sampling);
+ const orphanReport = structuredClone(reported); delete orphanReport.scene.sampling.layerPlan;
+ assert.throws(() => readCompactCompiler(orphanReport), /report requires its retained plan/);
+ const wrongReport = structuredClone(reported); wrongReport.scene.sampling.layerOptimization.plannedLayers++;
+ assert.throws(() => readCompactCompiler(wrongReport), /report differs from its plan/);
+ const wrongCount = structuredClone(planned); wrongCount.scene.sampling.sliceCounts.x++;
+ assert.throws(() => readCompactCompiler(wrongCount), /differs from its retained layer plan/);
+ const wrongQuadrature = structuredClone(planned); wrongQuadrature.scene.sampling.layerPlan.referenceSamplesPerSlab = 2;
+ assert.throws(() => readCompactCompiler(wrongQuadrature), /differs from its retained layer plan/);
+});
diff --git a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.ts b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.ts
index d569b81f55..895c3fdc9c 100644
--- a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.ts
+++ b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.ts
@@ -1,3 +1,4 @@
+import { CSS_COMPILER_RENDER_BUDGET } from '../../../../../../../../src/renderers/css/volume/compiler-render-budget.ts';
/** cssEarth representation adapter; compact replay does not depend on research processing. */
import { replayCompactCompiler as replay, type CompactCompilerBackend } from '@cssearth/volume-bake/compact-inputs/compiler';
import type { CompilerBakeProgress } from '@cssearth/volume-bake/compiler/bake';
@@ -10,6 +11,7 @@ export { readCompactCompiler } from '@cssearth/volume-bake/compact-inputs/compil
export function replayCompactCompiler(root: string, pin: Pin, outputDirectory: string,
progress?: (progress: CompilerBakeProgress) => void) {
const backend: CompactCompilerBackend = {
+ renderBudget: CSS_COMPILER_RENDER_BUDGET,
compileVolume: input => validatePreparedCssVolume(compileCssVolume({ ...input, recipe: { anchors: [] } })),
readVolume: validatePreparedCssVolume,
prepareStarSprites: prepareCompilerStarSprites,
diff --git a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.test.ts b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.test.ts
index 83478c89a2..5891dc1509 100644
--- a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.test.ts
+++ b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.test.ts
@@ -3,10 +3,11 @@ import assert from "node:assert/strict";
import { mkdtemp, mkdir, readFile, writeFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
-import { gunzipSync } from "node:zlib";
+import { gunzipSync, gzipSync } from "node:zlib";
import { createHash } from "node:crypto";
import { replayCompactSampled } from "./compact-sampled.ts";
import { jointRecord } from "../../../features/joint-fit/model.ts";
+import { maximumPlanningEmission, prepareCompactSampledInputs } from '@cssearth/volume-bake/compact-inputs/sampled';
test("compact sampled replay rejects changed measured particles before any reconstruction", async () => {
const root = await mkdtemp(join(tmpdir(), "nebula-sampled-"));
try {
@@ -36,3 +37,32 @@ test("compact sampled replay rejects changed measured particles before any recon
await rm(root, { recursive: true, force: true });
}
});
+
+test('sampled planning preserves features unique to any lens without changing its emission', () => {
+ const reference = (x: number, _y: number, _z: number, out: [number, number, number]) => { out.fill(x < 0 ? 3 : 0); };
+ const secondary = (x: number, _y: number, _z: number, out: [number, number, number]) => { out.fill(x > 0 ? 7 : 1); };
+ const planning = maximumPlanningEmission([reference, secondary]), reverse = maximumPlanningEmission([secondary, reference]);
+ const out: [number, number, number] = [0, 0, 0], alternate: [number, number, number] = [0, 0, 0];
+ for (const [x, expected] of [[-1, 3], [0, 1], [1, 7]] as const) {
+ planning(x, 0, 0, out); reverse(x, 0, 0, alternate);
+ assert.deepEqual(out, [expected, expected, expected]); assert.deepEqual(alternate, out);
+ }
+ reference(1, 0, 0, out); assert.deepEqual(out, [0, 0, 0]);
+ secondary(-1, 0, 0, out); assert.deepEqual(out, [1, 1, 1]);
+ assert.throws(() => maximumPlanningEmission([]), /at least one/);
+ const invalid = maximumPlanningEmission([reference, (_x, _y, _z, rgb) => { rgb[0] = 1; }]);
+ assert.throws(() => invalid(1, 0, 0, out), /finite nonnegative/);
+});
+
+test('retained sampled loader rejects a renamed scene before reading particles or writing outputs', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'nebula-retained-sampled-'));
+ try {
+ const value: unknown = JSON.parse(gunzipSync(await readFile('src/objects/m1/source/compact/model.json.gz')).toString());
+ assert.ok(jointRecord(value)); value.sourceResult = '0'.repeat(64);
+ const bytes = gzipSync(Buffer.from(JSON.stringify(value))), path = 'input.json.gz';
+ await writeFile(join(root, path), bytes);
+ await assert.rejects(prepareCompactSampledInputs(root,
+ { path, sha256: createHash('sha256').update(bytes).digest('hex') },
+ { decodeFits() { throw new Error('Must reject before decoding'); } }), /source result differs/);
+ } finally { await rm(root, { recursive: true, force: true }); }
+});
diff --git a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.ts b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.ts
index b3c84c86fc..f6f3f604a1 100644
--- a/labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.ts
+++ b/labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.ts
@@ -1,3 +1,4 @@
+import { CSS_COMPILER_RENDER_BUDGET } from '../../../../../../../../src/renderers/css/volume/compiler-render-budget.ts';
import { replayCompactSampled as replay } from '@cssearth/volume-bake/compact-inputs/sampled';
import { compileCssVolume } from '../../../../../../../../src/renderers/css/preparation/volume.ts';
import { validatePreparedCssVolume } from '../../../../../../../../src/renderers/css/volume/validation.ts';
@@ -187,6 +188,7 @@ export async function exportCompactSampled(
}
export async function replayCompactSampled(root: string, inputPin: CompilerPin, outputDirectory: string) {
return replay(root, inputPin, outputDirectory, {
+ renderBudget: CSS_COMPILER_RENDER_BUDGET,
compileVolume: input => compileCssVolume({ ...input, recipe: { anchors: [] } }),
validateVolume: validatePreparedCssVolume,
prepareStarSprites: prepareCompilerStarSprites,
diff --git a/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-frame.test.ts b/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-frame.test.ts
index c6e82424ac..4f14b5bdfb 100644
--- a/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-frame.test.ts
+++ b/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-frame.test.ts
@@ -4,6 +4,9 @@ import { embedNebulaFrame, embedNebulaVolume, reflectNebulaPoint, ARCSECOND_RADI
import { worldRotationFromQuaternion } from '../../../../../../../../src/renderers/css/navigation/world-camera-math.ts';
import { validatePreparedCssVolume } from '../../../../../../../../src/renderers/css/volume/validation.ts';
import type { PreparedCssVolume } from '../../../../../../../../src/renderers/css/volume/types.ts';
+import { compileCssVolume } from '../../../../../../../../src/renderers/css/preparation/volume.ts';
+import { compilerFrame, compilerPreparedPoint, compilerPreparedSlices } from '@cssearth/volume-core/coordinates/compiler-frame';
+import type { VolumeSlices } from '@cssearth/volume-core/contracts/volume-slices';
const frame = { referenceFrame:'lab',epochJdTt:0,originM:[0,0,0] as const,localToReferenceXyzw:[0,0,0,1] as const,
metersPerUnit:1,boundsUnits:{min:[-3,-2,-1] as const,max:[4,2,1] as const} };
const sky = { centerIcrsDegrees:[0,0] as [number,number],distancePc:100,imageRotationDegrees:0,arcsecPerUnit:1 };
@@ -35,3 +38,40 @@ test('invalid physical frames cannot silently assign units or a sky direction',(
for(const change of [{distancePc:0},{arcsecPerUnit:NaN},{centerIcrsDegrees:[360,0] as [number,number]},{imageRotationDegrees:Infinity}])
assert.throws(()=>embedNebulaFrame(frame,{...sky,...change}),/Invalid physical/);
});
+
+test('new physical compiler transport and historical source transport produce identical app sky geometry', () => {
+ const bounds = { min: [-4, -3, -2] as [number, number, number], max: [4, 3, 2] as [number, number, number] };
+ const angular = compilerFrame(bounds), physical = compilerFrame(bounds, true);
+ const slices: VolumeSlices = { boundsUnits: bounds, quads: [{ id: 'z-0', axis: 'z', sliceIndex: 0,
+ texturePath: 'a.png', widthPx: 2, heightPx: 2, vertices: [[-2, 2, -1], [2, 2, -1], [2, -2, -1], [-2, -2, -1]],
+ uvs: [[0, 0], [1, 0], [1, 1], [0, 1]], center: [0, 0, -1], normal: [0, 0, 1], sha256: 'a'.repeat(64), bytes: 12, alphaCoverage: 1 }],
+ provenance: {}, approximation: { method: 'fixture', radialEmission: 'none', limitations: [], samplesPerSlab: 4,
+ opticalWeight: 1, exposureGain: 1, sliceCounts: { x: 1, y: 1, z: 1 }, slabPitchUnits: { x: 8, y: 6, z: 4 } } };
+ for (const axis of ['x', 'y'] as const) slices.quads.push({ ...slices.quads[0]!, id: `${axis}-0`, axis,
+ normal: axis === 'x' ? [1, 0, 0] : [0, 1, 0], alphaCoverage: 0 });
+ const oldBank = compileCssVolume({ id: 'angular', frame: angular.frame, slices, recipe: { anchors: [] } });
+ const newBank = compileCssVolume({ id: 'physical', frame: physical.frame, slices: compilerPreparedSlices(slices), recipe: { anchors: [] } });
+ const oldApp = embedNebulaVolume(oldBank, embedNebulaFrame(angular.frame, sky), 'same', 'same');
+ const newApp = embedNebulaVolume(newBank, embedNebulaFrame(physical.frame, sky), 'same', 'same');
+ assert.deepEqual(newApp.frame, oldApp.frame);
+ assert.deepEqual(newApp.resources, oldApp.resources);
+ for (const axis of ['x', 'y', 'z'] as const) {
+ const before = oldApp.stacks.find(stack => stack.axis === axis)!, after = newApp.stacks.find(stack => stack.axis === axis)!;
+ assert.equal(after.leaves.length, before.leaves.length);
+ for (let i = 0; i < before.leaves.length; i++) {
+ const oldLeaf = before.leaves[i]!, newLeaf = after.leaves[i]!;
+ assert.deepEqual(newLeaf.centerUnits, oldLeaf.centerUnits);
+ assert.deepEqual(newLeaf.boundsCssPixels, oldLeaf.boundsCssPixels);
+ assert.equal(newLeaf.texturePath, oldLeaf.texturePath);
+ const matrix = (value: string) => value.slice(9, -1).split(',').map(Number);
+ const a = matrix(oldLeaf.style.transform), b = matrix(newLeaf.style.transform);
+ // Plane-local Z is always zero; compare every rendered corner, not its unused normal column.
+ for (const x of [0, Number.parseFloat(oldLeaf.style.width)]) for (const y of [0, Number.parseFloat(oldLeaf.style.height)])
+ for (const row of [0, 1, 2]) assert.ok(Math.abs(a[row]! * x + a[row + 4]! * y + a[row + 12]! -
+ b[row]! * x - b[row + 4]! * y - b[row + 12]!) < 1e-9);
+ }
+ }
+ for (const point of [[1, 2, -1], [-2, 1, 1]] as [number, number, number][]) {
+ assert.deepEqual(reflectNebulaPoint(compilerPreparedPoint(point), physical.frame), reflectNebulaPoint(point, angular.frame));
+ }
+});
diff --git a/labs/nebula/packages/lab/src/adapters/preparation/css-volume.ts b/labs/nebula/packages/lab/src/adapters/preparation/css-volume.ts
index 3e6f13137d..a5fc4052b2 100644
--- a/labs/nebula/packages/lab/src/adapters/preparation/css-volume.ts
+++ b/labs/nebula/packages/lab/src/adapters/preparation/css-volume.ts
@@ -1,3 +1,4 @@
/** Explicit cssEarth host integration; implementation remains with its source owner. */
export { compileCssVolume } from '../../../../../../../src/renderers/css/preparation/volume.ts';
export type { PreparedCssVolume } from '../../../../../../../src/renderers/css/preparation/volume.ts';
+export { CSS_COMPILER_RENDER_BUDGET } from '../../../../../../../src/renderers/css/volume/compiler-render-budget.ts';
diff --git a/labs/nebula/packages/lab/src/adapters/viewer/compiler-renderer.ts b/labs/nebula/packages/lab/src/adapters/viewer/compiler-renderer.ts
index 98cceb9bf4..aeb4448b2b 100644
--- a/labs/nebula/packages/lab/src/adapters/viewer/compiler-renderer.ts
+++ b/labs/nebula/packages/lab/src/adapters/viewer/compiler-renderer.ts
@@ -3,7 +3,7 @@ import { volumeRenderer } from './volume-renderer';
import type { CompilerViewerBackend } from '@cssearth/volume-viewer/scene/compiler-viewer';
import { presentPhysicalPoseInVolume } from '@cssearth/engine';
import { projectPreparedPoint } from '@cssearth/volume-viewer/camera/point-projection';
-import { transposeWorldRotation, worldRotationFromQuaternion } from '../../../../../../../src/renderers/css/navigation/world-camera-math';
+import { cssViewFromOrientation } from '../../../../../../../src/renderers/css/navigation/world-camera-math';
import type { PreparedCssVolume, VolumeCameraPublication } from '../../../../../../../src/renderers/css/volume/types';
import { assertCompilerBankIdentity, assertCompilerLensGeometry } from '../../server/workflows/compiler/bank-validation.ts';
@@ -13,10 +13,11 @@ export const compilerRenderer: CompilerViewerBackend r[row * 3]! * point[0]! + r[row * 3 + 1]! * point[1]! + r[row * 3 + 2]! * point[2]! + t[row]!);
const [px, py] = publication.viewport.principalOffsetPixels;
return [publication.viewport.widthPixels! / 2 + px + (eye[0]! - px) * focalPixels / (focalPixels - eye[2]!),
@@ -50,6 +52,20 @@ test('posed cloud stays centered, camera orientation is proper, and ordinary-dep
assert.throws(() => shapeCloudCamera(frame, image, viewport, { ...framing, zoom: 0 }, 0, 0), /positive extents/);
});
+test('prepared compact lights and slab geometry share the same physical camera projection', () => {
+ const viewport = { width: 900, height: 650 }, framing = { zoom: 1, panX: 21, panY: -17 };
+ for (const [yaw, pitch] of [[0, 0], [35, 27], [90, 0], [0, -89]]) {
+ const camera = shapeCloudCamera(frame, image, viewport, framing, yaw!, pitch!);
+ const stars = compilerRenderer.starProjection(camera, frame, viewport);
+ for (const pixel of [[160, 170], [530, 390]] as [number, number][]) {
+ const point = [(pixel[0] - image.width / 2) * image.unitsPerPixel,
+ (image.height / 2 - pixel[1]) * image.unitsPerPixel, -1];
+ const star = stars.project(point), volume = project(camera, pixel, -point[2]!);
+ assert.ok(Math.hypot(star.x + viewport.width / 2 - volume[0], star.y + viewport.height / 2 - volume[1]) < 1e-6);
+ }
+ }
+});
+
test('true orthographic CSS transport keeps exact image projection at every depth without large CSS translations', () => {
const viewport = { width: 900, height: 650 }, framing = { zoom: 2.5, panX: 21, panY: -17 };
const fit = Math.min(viewport.width / image.width, viewport.height / image.height) * .94 * framing.zoom;
@@ -60,10 +76,56 @@ test('true orthographic CSS transport keeps exact image projection at every dept
const cx = Math.cos(pitch! * Math.PI / 180), sx = Math.sin(pitch! * Math.PI / 180);
for (const z of [-5, 0, 5]) for (const [px, py] of [[0, 0], [768, 534], [213, 421]]) {
const x = (px! - image.width / 2) * image.unitsPerPixel, y = (image.height / 2 - py!) * image.unitsPerPixel;
- const point = [y * 50, x * 50, z * 50], r = camera.rotation;
+ const point = [y * 50, x * 50, -z * 50], r = camera.rotation;
const actual = [0, 1].map(row => (r[row * 3]! * point[0]! + r[row * 3 + 1]! * point[1]! + r[row * 3 + 2]! * point[2]!) * camera.scale);
const expected = [(cy * x - sy * z) * fit / image.unitsPerPixel, (sx * sy * x - cx * y + sx * cy * z) * fit / image.unitsPerPixel];
assert.ok(Math.hypot(actual[0]! - expected[0]!, actual[1]! - expected[1]!) < 1e-9);
}
}
});
+
+test('negative source-away depth is nearer and magnifies more than positive source-away depth', () => {
+ const viewport = { width: 900, height: 650 }, framing = { zoom: 1, panX: 0, panY: 0 };
+ const camera = shapeCloudCamera(frame, image, viewport, framing, 0, 0);
+ assert.ok(camera.world.pose.positionM[2] > 0, 'The physical toward-axis observer must lie at positive local Z.');
+ const radius = (sourceAway: number) => {
+ const point = project(camera, [160, 170], sourceAway);
+ return Math.hypot(point[0] - viewport.width / 2, point[1] - viewport.height / 2);
+ };
+ assert.ok(radius(-5) > radius(0) && radius(0) > radius(5), 'Near source points must magnify more; this is independent of legacy camera formulas.');
+});
+
+test('historical compiler banks transport retained source depth once and agree with their stars', () => {
+ const oldFrame = { ...frame, referenceFrame: 'lab-sky-angular' };
+ const bank: PreparedCssVolume = { schema: 'cssearth-css-volume@1', id: 'old', frame: oldFrame, anchors: [],
+ resources: [], provenance: {}, approximation: {}, stacks: [{ axis: 'z', leaves: [{ id: 'near', centerUnits: [1, 2, -3],
+ texturePath: 'pinned.png', widthPx: 1, heightPx: 1, boundsCssPixels: { min: [1, 2, -151], max: [3, 4, -149] },
+ style: { width: '1px', height: '1px', transform: 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,100,50,-150,1)',
+ backgroundSize: '1px 1px', backgroundPosition: '0px 0px' } }] }] };
+ const before = structuredClone(bank), actual = physicalLabBank(bank);
+ assert.deepEqual(bank, before);
+ assert.deepEqual(actual.stacks[0]!.leaves[0]!.centerUnits, [1, 2, 3]);
+ assert.equal(actual.frame.referenceFrame, 'lab-sky-west-north-toward');
+ assert.match(actual.stacks[0]!.leaves[0]!.style.transform, /100,50,150,1\)$/);
+ assert.equal(physicalLabBank({ ...bank, frame }).stacks, bank.stacks, 'New prepared geometry must not reflect again.');
+ const viewport = { width: 900, height: 650 }, framing = { zoom: 1, panX: 0, panY: 0 };
+ const oldCamera = compilerRenderer.camera(oldFrame, image, viewport, framing, 35, 27).publication;
+ const newCamera = compilerRenderer.camera(frame, image, viewport, framing, 35, 27).publication;
+ assert.equal(oldCamera.world.referenceFrame, actual.frame.referenceFrame);
+ assert.doesNotThrow(() => preparedVolumeCameraTransform(oldCamera, actual.frame));
+ const historical = compilerRenderer.starProjection(oldCamera, oldFrame, viewport).project([1, 2, -3]);
+ const current = compilerRenderer.starProjection(newCamera, frame, viewport).project([1, 2, 3]);
+ assert.deepEqual(historical, current);
+});
+
+test('legacy Shapes and Joint-fit share the angular-to-physical mount and camera boundary', () => {
+ for (const referenceFrame of ['lab-sky-angular', 'lab-image-relative-unscaled']) {
+ const source = { ...frame, referenceFrame, boundsUnits: { min: [-1, -2, -3] as const, max: [4, 5, 6] as const } };
+ const actual = physicalLabFrame(source);
+ assert.deepEqual(actual.boundsUnits, { min: [-1, -2, -6], max: [4, 5, 3] });
+ const camera = volumeRenderer.camera(source, image, { width: 900, height: 650 }, { zoom: 1, panX: 0, panY: 0 }, 35, 27);
+ assert.equal(camera.publication.world.referenceFrame, actual.referenceFrame);
+ assert.doesNotThrow(() => preparedVolumeCameraTransform(camera.publication, actual));
+ assert.equal(source.referenceFrame, referenceFrame, 'Historical receipts stay unchanged.');
+ }
+});
diff --git a/labs/nebula/packages/lab/src/adapters/viewer/shape-cloud-renderer.ts b/labs/nebula/packages/lab/src/adapters/viewer/shape-cloud-renderer.ts
index 13c98b3948..b2fdb7622f 100644
--- a/labs/nebula/packages/lab/src/adapters/viewer/shape-cloud-renderer.ts
+++ b/labs/nebula/packages/lab/src/adapters/viewer/shape-cloud-renderer.ts
@@ -8,8 +8,12 @@ import type { VolumeCameraPublication } from '../../../../../../../src/renderers
export const shapeCloudCameraBackend: ShapeCloudCameraBackend = {
cssPixelsPerUnit: 50,
publication(frame, image, viewport, framing, rotation, distanceUnits, focalPixels) {
+ const local = worldRotationFromQuaternion(frame.localToReferenceXyzw);
+ // Prepared physical axes are west/north/toward. The source-facing CSS presentation has
+ // z away, so its mapping into the physical frame is a reflection, never a quaternion.
+ const presentationToReference = [local[0]!, local[1]!, -local[2]!, local[3]!, local[4]!, -local[5]!, local[6]!, local[7]!, -local[8]!];
const world = worldCameraFromCenteredPresentation({ rotation, distanceUnits }, {
- ...frame, presentationToReference: worldRotationFromQuaternion(frame.localToReferenceXyzw),
+ ...frame, presentationToReference,
bodyRadiusM: image.width * image.unitsPerPixel * frame.metersPerUnit / 2,
}, { focalPixels, principalOffsetPixels: [0, 0] });
return { world, viewport: { widthPixels: viewport.width, heightPixels: viewport.height, focalPixels,
diff --git a/labs/nebula/packages/lab/src/adapters/viewer/volume-renderer.ts b/labs/nebula/packages/lab/src/adapters/viewer/volume-renderer.ts
index 2770fbcd8b..8a5e21a748 100644
--- a/labs/nebula/packages/lab/src/adapters/viewer/volume-renderer.ts
+++ b/labs/nebula/packages/lab/src/adapters/viewer/volume-renderer.ts
@@ -4,6 +4,31 @@ import { mountPreparedCssVolume } from '../../../../../../../src/renderers/css/v
import { validatePreparedCssVolume } from '../../../../../../../src/renderers/css/volume/validation';
import type { PreparedCssVolume, VolumeCameraPublication } from '../../../../../../../src/renderers/css/volume/types';
import { shapeCloudOrthographicCamera } from './shape-cloud-camera';
+const legacyAngularFrame = (frame: PreparedCssVolume['frame']) => ['lab-sky-angular', 'lab-image-relative-unscaled'].includes(frame.referenceFrame);
+/** Rename the runtime-only transported frame; source receipts retain their original frame and pins. */
+export function physicalLabFrame(frame: PreparedCssVolume['frame']): PreparedCssVolume['frame'] {
+ if (!legacyAngularFrame(frame)) return frame;
+ return { ...frame, referenceFrame: frame.referenceFrame === 'lab-sky-angular' ? 'lab-sky-west-north-toward' : 'lab-image-west-north-toward',
+ boundsUnits: { min: [frame.boundsUnits.min[0], frame.boundsUnits.min[1], -frame.boundsUnits.max[2]],
+ max: [frame.boundsUnits.max[0], frame.boundsUnits.max[1], -frame.boundsUnits.min[2]] } };
+}
+/** Historical angular and image-relative Lab banks store source west/north/away coordinates. Transport their retained
+ * matrices into the same physical west/north/toward basis as current prepared banks. No pixels,
+ * vertices, texture layout, or correspondence are generated by this compatibility transport. */
+export function physicalLabBank(bank: PreparedCssVolume): PreparedCssVolume {
+ if (!legacyAngularFrame(bank.frame)) return bank;
+ return { ...bank, frame: physicalLabFrame(bank.frame), stacks: bank.stacks.map(stack => ({ ...stack, ...(stack.normalUnits ? { normalUnits: [stack.normalUnits[0], stack.normalUnits[1], -stack.normalUnits[2]] as const } : {}), leaves: stack.leaves.map(leaf => {
+ const match = /^matrix3d\(([^)]+)\)$/.exec(leaf.style.transform);
+ const matrix = match?.[1]?.split(',').map(Number);
+ if (!matrix || matrix.length !== 16 || !matrix.every(Number.isFinite)) throw new TypeError('Invalid historical compiler transform.');
+ for (const index of [2, 6, 10, 14]) matrix[index] = -matrix[index]!;
+ const b = leaf.boundsCssPixels;
+ return { ...leaf, centerUnits: [leaf.centerUnits[0], leaf.centerUnits[1], -leaf.centerUnits[2]] as const,
+ ...(b ? { boundsCssPixels: { min: [b.min[0], b.min[1], -b.max[2]] as const, max: [b.max[0], b.max[1], -b.min[2]] as const } } : {}),
+ style: { ...leaf.style, transform: `matrix3d(${matrix.join(',')})` } };
+ }) })) };
+}
+
export const volumeRenderer: VolumeViewerBackend = {
validateBank: validatePreparedCssVolume,
resources: bank => bank.resources,
@@ -11,7 +36,7 @@ export const volumeRenderer: VolumeViewerBackend (['x', 'y', 'z'] as const).flatMap(axis => bank.stacks.find(stack => stack.axis === axis)!
.leaves.map(leaf => leaf.texturePath)),
mount(host, before, payload, resolveResource) {
- const mounted = mountPreparedCssVolume({ host, before, payload, resolveResource });
+ const mounted = mountPreparedCssVolume({ host, before, payload: physicalLabBank(payload), resolveResource });
const cameras = [...host.querySelectorAll('.css-volume-camera')];
const scenes = [...host.querySelectorAll('.css-volume-scene')];
const materials = mounted.roots.flatMap((axisRoot, index) => {
@@ -30,5 +55,5 @@ export const volumeRenderer: VolumeViewerBackend {
+ const directory = await realpath(await mkdtemp(join(tmpdir(), 'nebula-module-url-')));
+ const dependency = join(directory, 'dependency.ts'), entry = join(directory, 'entry.ts'), outfile = join(directory, 'bundle.mjs');
+ const guard = `import { pathToFileURL } from 'node:url';
+ const direct = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;`;
+ try {
+ await writeFile(dependency, `${guard}
+ if (direct) throw new Error('An imported command ran its entry guard');
+ export const source = import.meta.url;
+ export const spaced = import . meta . url;
+ export const bracket = import.meta['url'];
+ export const literal = "import.meta.url";
+ export const template = \`literal import.meta.url / \${import.meta.url}\`;
+ export const pattern = /import.meta.url/.source;`);
+ await writeFile(entry, `${guard}
+ import { source, spaced, bracket, literal, template, pattern } from './dependency.ts';
+ if (direct) console.log(JSON.stringify({ source, spaced, bracket, literal, template, pattern }));`);
+ await buildLabModule({ entryPoints: [entry], outfile, bundle: true, platform: 'node', format: 'esm', packages: 'external' });
+ const result = spawnSync(process.execPath, [outfile], { encoding: 'utf8', timeout: 10_000 });
+ assert.equal(result.status, 0, result.stderr);
+ const source = pathToFileURL(dependency).href;
+ assert.deepEqual(JSON.parse(result.stdout), { source, spaced: source, bracket: source, literal: 'import.meta.url',
+ template: `literal import.meta.url / ${source}`, pattern: 'import.meta.url' });
+ } finally { await rm(directory, { recursive: true, force: true }); }
+});
diff --git a/labs/nebula/packages/lab/src/cli/build.ts b/labs/nebula/packages/lab/src/cli/build.ts
index 43159c41ba..419ddefb68 100644
--- a/labs/nebula/packages/lab/src/cli/build.ts
+++ b/labs/nebula/packages/lab/src/cli/build.ts
@@ -1,9 +1,39 @@
/** Bundle internal TypeScript owners; native and third-party dependencies remain external. */
+import { readFile, realpath } from 'node:fs/promises';
import { createRequire } from 'node:module';
-import { resolve } from 'node:path';
-import { build, type BuildOptions } from 'esbuild';
+import { dirname, resolve } from 'node:path';
+import { pathToFileURL } from 'node:url';
+import { build, type BuildOptions, type Loader } from 'esbuild';
+import ts from 'typescript';
-export function buildLabModule(options: BuildOptions) {
+const LOADERS: Record = { '.ts': 'ts', '.mts': 'ts', '.cts': 'ts', '.tsx': 'tsx', '.js': 'js', '.mjs': 'js', '.cjs': 'js', '.jsx': 'jsx' };
+
+/** Replace only syntax: source text may also mention the expression inside strings, templates or regexes. */
+function sourceUrls(text: string, path: string): string {
+ const source = ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true);
+ const replacements: { start: number; end: number }[] = [];
+ const visit = (node: ts.Node) => {
+ const expression = ts.isPropertyAccessExpression(node) && node.name.text === 'url' ? node.expression
+ : ts.isElementAccessExpression(node) && ts.isStringLiteralLike(node.argumentExpression) && node.argumentExpression.text === 'url'
+ ? node.expression : undefined;
+ if (expression && ts.isMetaProperty(expression) && expression.keywordToken === ts.SyntaxKind.ImportKeyword) {
+ replacements.push({ start: node.getStart(source), end: node.end });
+ } else ts.forEachChild(node, visit);
+ };
+ visit(source);
+ const url = JSON.stringify(pathToFileURL(path).href);
+ for (const { start, end } of replacements.reverse()) text = text.slice(0, start) + url + text.slice(end);
+ return text;
+}
+
+export async function buildLabModule(options: BuildOptions) {
+ const inputs = Array.isArray(options.entryPoints)
+ ? options.entryPoints.map(entry => typeof entry === 'string' ? entry : entry.in)
+ : Object.values(options.entryPoints ?? {});
+ const entries = new Set(await Promise.all(inputs.map(async entry => {
+ const path = resolve(options.absWorkingDir ?? process.cwd(), entry);
+ return options.preserveSymlinks ? path : realpath(path);
+ })));
return build({ ...options, plugins: [{
name: 'nebula-workspace-source-owners',
setup(builder) {
@@ -13,5 +43,22 @@ export function buildLabModule(options: BuildOptions) {
return { path: require.resolve(args.path) };
});
},
+ }, {
+ // Bundling rewrites every module's `import.meta.url` to the single output file, which breaks the
+ // standard `import.meta.url === pathToFileURL(process.argv[1]).href` entry-point guard: a bundled
+ // command-line module sees its own URL equal to the program being run and executes its argument
+ // parser, throwing a usage error before the real entry does anything. Give each bundled module its
+ // own source URL back, so only the true entry point still matches `process.argv[1]`.
+ name: 'nebula-module-source-url',
+ setup(builder) {
+ builder.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async args => {
+ if (entries.has(args.path)) return undefined;
+ const text = await readFile(args.path, 'utf8');
+ if (!text.includes('import')) return undefined;
+ const extension = args.path.slice(args.path.lastIndexOf('.'));
+ return { contents: sourceUrls(text, args.path),
+ loader: LOADERS[extension] ?? 'ts', resolveDir: dirname(args.path) };
+ });
+ },
}, ...(options.plugins ?? [])] });
}
diff --git a/labs/nebula/packages/lab/src/cli/commands.ts b/labs/nebula/packages/lab/src/cli/commands.ts
index 1b75c402ad..bcac5904e4 100644
--- a/labs/nebula/packages/lab/src/cli/commands.ts
+++ b/labs/nebula/packages/lab/src/cli/commands.ts
@@ -87,5 +87,6 @@ export const labCommands: Record = {
"browser-reconstruction-reference": "labs/nebula/packages/lab/browser/browser-reconstruction-reference.ts",
"browser-reconstruction-tabs": "labs/nebula/packages/lab/browser/browser-reconstruction-tabs.ts",
"browser-lens-levels": "labs/nebula/packages/lab/browser/browser-lens-levels.ts",
- "browser-difference-map": "labs/nebula/packages/lab/browser/browser-difference-map.ts"
+ "browser-difference-map": "labs/nebula/packages/lab/browser/browser-difference-map.ts",
+ "browser-lens-radial": "labs/nebula/packages/lab/browser/browser-lens-radial.ts"
};
diff --git a/labs/nebula/packages/lab/src/cli/commands/acquire-images.ts b/labs/nebula/packages/lab/src/cli/commands/acquire-images.ts
index a00a43db95..40102bd63e 100644
--- a/labs/nebula/packages/lab/src/cli/commands/acquire-images.ts
+++ b/labs/nebula/packages/lab/src/cli/commands/acquire-images.ts
@@ -12,8 +12,14 @@ interface ReferenceImageRecipe {
schema: 'cssearth-lab-reference-images@1';
images: { id: string; url: string; sha256: string; bytes: number; width: number; height: number;
publisherUrl: string; credit: string; license: string;
- output: { path: string; quality: number; effort: number; sha256: string } }[];
+ // `resizeWidth` is for a source libwebp cannot encode at native size: the VST Omega Centauri mosaic
+ // (14540px of a crowded star field) overflows the encoder at every quality, while 8192px encodes. It only
+ // ever reduces, and the recorded interpretation states the reduction, so a downscale is never silent.
+ output: { path: string; quality: number; effort: number; sha256: string; resizeWidth?: number } }[];
}
+/** sharp reports the sample type; a provenance record states the bit depth a reader recognises. */
+const DEPTH_WORDS: Record = { uchar: '8-bit', ushort: '16-bit', ufloat: '32-bit float', float: '32-bit float' };
+
async function hash(path: string) {
const digest = createHash('sha256');
for await (const part of createReadStream(path)) digest.update(part);
@@ -42,12 +48,21 @@ for (const image of recipe.images) {
if (metadata.width !== image.width || metadata.height !== image.height) throw new Error(`Reference dimensions differ: ${image.id}`);
const output = resolve(image.output.path);
await mkdir(dirname(output), { recursive: true });
- await sharp(original).rotate().toColourspace('srgb').removeAlpha()
+ const resizeWidth = image.output.resizeWidth;
+ if (resizeWidth !== undefined && !(Number.isInteger(resizeWidth) && resizeWidth > 0 && resizeWidth < image.width)) {
+ throw new TypeError(`Reference resizeWidth must be a positive integer below the native width: ${image.id}`);
+ }
+ const pipe = sharp(original).rotate().toColourspace('srgb').removeAlpha();
+ await (resizeWidth === undefined ? pipe : pipe.resize({ width: resizeWidth }))
.webp({ quality: image.output.quality, effort: image.output.effort }).toFile(`${output}.part`);
if (await hash(`${output}.part`) !== image.output.sha256) throw new Error(`Reference derivative pin differs: ${image.id}`);
await rename(`${output}.part`, output);
await writeFile(`${output}.json`, JSON.stringify({ schema: 'cssearth-lab-reference-image@1', source: image,
- interpretation: 'Full spatial resolution, converted from publisher 16-bit TIFF to 8-bit sRGB WebP. Lossy display reference; no crop or resize. Original TIFF remains in the ignored cache.',
+ interpretation: `Converted from the publisher's ${DEPTH_WORDS[metadata.depth ?? ''] ?? `${metadata.depth ?? 'unknown'}-sample`} TIFF to 8-bit sRGB WebP. Lossy display reference; never cropped.${
+ resizeWidth === undefined
+ ? ' Full spatial resolution: not resized.'
+ : ` Reduced from ${image.width}px to ${resizeWidth}px wide because libwebp cannot encode this source at native size; measure angular scale from the recorded native dimensions, not from this file.`
+ } Original TIFF remains in the ignored cache.`,
outputBytes: (await stat(output)).size }, null, 2) + '\n');
console.log(`REFERENCE_READY ${image.id}: ${image.width}x${image.height}, ${image.output.path}`);
}
diff --git a/labs/nebula/packages/lab/src/cli/commands/compile-candidates.ts b/labs/nebula/packages/lab/src/cli/commands/compile-candidates.ts
index d2e5a20bbd..047e400b27 100644
--- a/labs/nebula/packages/lab/src/cli/commands/compile-candidates.ts
+++ b/labs/nebula/packages/lab/src/cli/commands/compile-candidates.ts
@@ -8,6 +8,7 @@ import { compileNebula, validateCompilerResult } from '../../server/workflows/co
import { jointRecord, jointPath } from '../../features/joint-fit/model.ts';
import { loadDepthModel } from '../../server/workflows/compiler/depth-model.ts';
import { sampledOwnerPins } from '../../features/sampled-prior/ownership.ts';
+import { loadPhotometricPrior } from '../../server/workflows/compiler/photometric-prior.ts';
const [cataloguePath, ...args] = process.argv.slice(2);
if (!cataloguePath || args.some(arg => arg !== '--alignment-only' && !/^--object=[a-z0-9-]+$/.test(arg))) {
@@ -70,6 +71,8 @@ for (const candidate of selected) {
if (recipe.observedStars) inputPaths.push(recipe.observedStars.path);
const depth = recipe.depthRecipe ? await loadDepthModel(root, recipe.depthRecipe, recipe.id) : undefined;
if (depth) inputPaths.push(depth.recipePath, depth.recipe.evidence.path);
+ const photometric = recipe.photometricPriorRecipe ? await loadPhotometricPrior(root, recipe.photometricPriorRecipe, recipe.id) : undefined;
+ if (photometric && recipe.photometricPriorRecipe) inputPaths.push(recipe.photometricPriorRecipe, photometric.recipe.evidence.path);
const method: unknown = JSON.parse(await readFile(result.method.path, 'utf8'));
if (!jointRecord(method) || !Array.isArray(method.implementation)) throw new TypeError('Missing compiler implementation identity.');
const sampledOwners = recipe.sampledRecipe ? sampledOwnerPins(method, recipe.sampledRecipe) : [];
@@ -77,6 +80,9 @@ for (const candidate of selected) {
if (depth && (!jointRecord(method.physicalDepth) || !jointRecord(method.physicalDepth.recipe) || !jointRecord(method.physicalDepth.evidence) ||
method.physicalDepth.recipe.sha256 !== depth.recipeSha256 || method.physicalDepth.evidence.sha256 !== depth.recipe.evidence.sha256))
throw new TypeError('Compiled depth sources differ from the configured recipe or evidence.');
+ if (photometric && (!jointRecord(method.photometricPrior) || !jointRecord(method.photometricPrior.recipe) || !jointRecord(method.photometricPrior.evidence) ||
+ method.photometricPrior.recipe.sha256 !== photometric.recipeSha256 || method.photometricPrior.evidence.sha256 !== photometric.recipe.evidence.sha256))
+ throw new TypeError('Compiled photometric sources differ from the configured model or evidence.');
for (const owner of method.implementation) {
if (!jointRecord(owner) || !jointPath(owner.path) || typeof owner.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(owner.sha256))
throw new TypeError('Invalid compiler implementation owner. Compile again with the current implementation.');
@@ -90,6 +96,9 @@ for (const candidate of selected) {
if (depth && (inputs.find(source => source.path === depth.recipePath)?.sha256 !== depth.recipeSha256 ||
inputs.find(source => source.path === depth.recipe.evidence.path)?.sha256 !== depth.recipe.evidence.sha256))
throw new TypeError('Depth inputs changed during publication. Compile again.');
+ if (photometric && (inputs.find(source => source.path === recipe.photometricPriorRecipe)?.sha256 !== photometric.recipeSha256 ||
+ inputs.find(source => source.path === photometric.recipe.evidence.path)?.sha256 !== photometric.recipe.evidence.sha256))
+ throw new TypeError('Photometric inputs changed during publication. Compile again.');
await save(resolve(published, `${candidate.id}.json`), { schema: 'cssearth-nebula-compiler-published@1', recipePath: candidate.compilerRecipe,
result: resultPin, inputs });
results.push({ id: candidate.id, status: 'complete', result: resultPin, metrics: result.metrics, seconds: (performance.now() - started) / 1000 });
diff --git a/labs/nebula/packages/lab/src/features/compiler/compiler-panel.tsx b/labs/nebula/packages/lab/src/features/compiler/compiler-panel.tsx
index bc7c1be5e2..baec51ecdb 100644
--- a/labs/nebula/packages/lab/src/features/compiler/compiler-panel.tsx
+++ b/labs/nebula/packages/lab/src/features/compiler/compiler-panel.tsx
@@ -50,6 +50,7 @@ function CompilerSession({ recipePath, cataloguePath, observationManifest, publi
const controlsChosen = useRef(initialControls !== null);
const [controls, setControls] = useState(initialControls ?? { ...defaultCompilerControls });
const [fixedGeometry, setFixedGeometry] = useState(null);
+ const [fixedDepth, setFixedDepth] = useState(false);
const [configuredWeights, setConfiguredWeights] = useState>({});
const [recipeError, setRecipeError] = useState('');
const [presentation, setPresentation] = useState(() => savedPresentation(`${storageKey}:view`));
@@ -71,8 +72,11 @@ function CompilerSession({ recipePath, cataloguePath, observationManifest, publi
}
const sampled = Boolean(configured.value.sampledRecipe);
setFixedGeometry(sampled);
+ const photometric = Boolean(configured.value.photometricPriorRecipe);
+ setFixedDepth(photometric);
// Sampled geometry has no editable fit controls. Preserve saved fit controls for the standard operator.
if (sampled || !controlsChosen.current) setControls(compilerControlsForRecipe(configured.value));
+ else if (photometric) setControls(previous => ({ ...previous, depth: 1 }));
setConfiguredWeights(configured.value.sourceWeights ?? {});
if (structures.status === 'fulfilled') setCatalogue(structures.value);
if (manifest.status === 'fulfilled') setObservations(manifest.value);
@@ -176,8 +180,9 @@ function CompilerSession({ recipePath, cataloguePath, observationManifest, publi
title="Retain more prepared small-scale image structure in the inferred cloud." onChange={detail => updateControls({ ...controls, detail })} />
updateControls({ ...controls, faint })} />
- updateControls({ ...controls, depth })} />
+ {!fixedDepth && updateControls({ ...controls, depth })} />}
+ {fixedDepth && Depth is an inferred realization of the published model.
}
{result ? 'Controls update automatically' : 'Prepared sources restored on compile'}
>}
{fixedGeometry && Fixed reconstructed geometry
}
diff --git a/labs/nebula/packages/lab/src/features/compiler/compiler-published.test.ts b/labs/nebula/packages/lab/src/features/compiler/compiler-published.test.ts
index f606ef05be..84e31df9d6 100644
--- a/labs/nebula/packages/lab/src/features/compiler/compiler-published.test.ts
+++ b/labs/nebula/packages/lab/src/features/compiler/compiler-published.test.ts
@@ -63,18 +63,18 @@ test('saved clouds remain inspectable after producer code changes without rewrit
});
/** Minimal valid metadata receipt; texture decoding belongs to the volume runtime tests. */
-function completedFixture(options: { depth?: boolean; depthId?: string; ledgerId?: string; declaredEvidenceHash?: string; omitMethodDepth?: boolean; staleSnapshot?: boolean } = {}) {
+function completedFixture(options: { depth?: boolean; photometric?: boolean; depthId?: string; ledgerId?: string; declaredEvidenceHash?: string; omitMethodDepth?: boolean; staleSnapshot?: boolean } = {}) {
const data = new Map(files), id = '1'.repeat(64), directory = `.local/nebula-lab/compiler/${id}`;
const depthPath = 'labs/nebula/models/example/depth.json', evidencePath = 'labs/nebula/models/example/evidence.json';
- const compiler = { ...recipe, ...(options.depth ? { depthRecipe: depthPath } : {}) };
+ const compiler = { ...recipe, ...(options.depth ? { depthRecipe: depthPath } : {}), ...(options.photometric ? { photometricPriorRecipe: depthPath } : {}) };
data.set(recipePath, JSON.stringify(compiler));
const inputPaths = [...data.keys()];
const put = (path: string, value: unknown) => { const bytes = JSON.stringify(value); data.set(path, bytes); return { path, sha256: digest(bytes) }; };
let physicalDepth: { recipe: { path: string; sha256: string }; evidence: { path: string; sha256: string } } | undefined;
- if (options.depth) {
+ if (options.depth || options.photometric) {
const ledger = { schema: 'cssearth-nebula-physical-evidence@1', subjectId: options.ledgerId ?? recipe.id, sources: [], evidence: [], methods: [] };
const evidence = put(evidencePath, ledger);
- const depth = { schema: 'cssearth-nebula-depth-model@1', id: options.depthId ?? recipe.id,
+ const depth = { schema: options.photometric ? 'cssearth-photometric-mge@1' : 'cssearth-nebula-depth-model@1', id: options.depthId ?? recipe.id,
evidence: { ...evidence, sha256: options.declaredEvidenceHash ?? evidence.sha256 } };
put(depthPath, depth); inputPaths.push(depthPath, evidencePath);
physicalDepth = { recipe: put(`${directory}/depth.json`, options.staleSnapshot ? { ...depth, historical: true } : depth),
@@ -84,7 +84,7 @@ function completedFixture(options: { depth?: boolean; depthId?: string; ledgerId
const method = put(`${directory}/method.json`, { recipeSha256: digest(data.get(recipePath)!), implementation: [],
request: { action: 'apply', imageId: 'compiler', recipePath, cataloguePath: recipe.structureCatalogue,
imageToFrame: {}, evidence: { sensitivity: 1, weights: [] }, controls },
- ...(!options.omitMethodDepth && physicalDepth ? { physicalDepth } : {}) });
+ ...(!options.omitMethodDepth && physicalDepth ? options.photometric ? { photometricPrior: physicalDepth } : { physicalDepth } : {}) });
const blob = put(`${directory}/placeholder.json`, {}), bounds = { min: [-1, -1, -1] as [number, number, number], max: [1, 1, 1] as [number, number, number] };
const sky = { min: [-1, -1] as [number, number], max: [1, 1] as [number, number] };
const result: CompilerResult = { schema: 'cssearth-nebula-compiler-result@1', id, label: 'Example', defaultSourceId: 'optical', controls, pipeline: [],
@@ -108,6 +108,20 @@ test('legacy and fully pinned depth receipts both restore without processing', a
}
});
+test('photometric publication requires the exact model and evidence used for the saved volume', async () => {
+ const good = completedFixture({ photometric: true });
+ assert.equal((await loadPublishedCompiler(pointer, recipePath, good.fetchLocal))?.id, good.result.id);
+ for (const target of ['depthPath', 'evidencePath'] as const) {
+ const missing = completedFixture({ photometric: true });
+ missing.receipt.inputs = missing.receipt.inputs.filter(pin => pin.path !== missing[target]);
+ await assert.rejects(loadPublishedCompiler(pointer, recipePath, missing.fetchLocal), /configured source|configured evidence/);
+ }
+ for (const options of [{ omitMethodDepth: true }, { depthId: 'other' }, { staleSnapshot: true }]) {
+ const bad = completedFixture({ photometric: true, ...options });
+ await assert.rejects(loadPublishedCompiler(pointer, recipePath, bad.fetchLocal), /configured photometric model|configured evidence/);
+ }
+});
+
test('a current publication can replace a saved result only for the same controls, evidence and registration', async () => {
const fixture = completedFixture();
const expected = readCompilerRequest({ action: 'apply', imageId: 'compiler', recipePath, cataloguePath: recipe.structureCatalogue,
diff --git a/labs/nebula/packages/lab/src/features/compiler/compiler-published.ts b/labs/nebula/packages/lab/src/features/compiler/compiler-published.ts
index 457a699445..3796378bd2 100644
--- a/labs/nebula/packages/lab/src/features/compiler/compiler-published.ts
+++ b/labs/nebula/packages/lab/src/features/compiler/compiler-published.ts
@@ -8,8 +8,8 @@ interface Published { recipePath: string; result: Pin; inputs: Pin[] }
/** Producer identities are historical metadata; scientific recipes/outputs never match this policy. */
function producerPath(path: string): boolean {
return /^labs\/nebula\/(?:src\/.+\.[cm]?tsx?|packages\/(?:lab|volume-core|volume-bake|reconstruction)\/(?:src\/.+\.[cm]?tsx?|package\.json))$/.test(path) ||
- /^src\/(?:preparation|renderers)\/.+\.[cm]?ts$/.test(path) ||
- /^tools\/(?:fits\.mts|nebula\/application\/.+\.[cm]?ts)$/.test(path);
+ /^src\/(?:preparation|renderers|platform)\/.+\.[cm]?ts$/.test(path) ||
+ /^tools\/(?:(?:fits|fits-sky|source-values)\.mts|(?:nebula\/application|objects)\/.+\.[cm]?ts)$/.test(path);
}
const record = (value: unknown): value is Record => value !== null && typeof value === 'object' && !Array.isArray(value);
function pin(value: unknown): Pin {
@@ -47,7 +47,8 @@ export async function loadPublishedCompiler(path: string, recipePath: string, fe
const recipeBytes = inputs.find(([path]) => path === recipePath)![1];
const recipe = readCompilerRecipe(JSON.parse(new TextDecoder().decode(recipeBytes)));
const required = [recipe.observationRecipe, recipe.observationCatalogue, recipe.structureRecipe, recipe.structureCatalogue,
- ...(recipe.observedStars ? [recipe.observedStars.path] : []), ...(recipe.jointRecipe ? [recipe.jointRecipe] : []), ...(recipe.depthRecipe ? [recipe.depthRecipe] : []), ...(recipe.sampledRecipe ? [recipe.sampledRecipe] : [])];
+ ...(recipe.observedStars ? [recipe.observedStars.path] : []), ...(recipe.jointRecipe ? [recipe.jointRecipe] : []), ...(recipe.depthRecipe ? [recipe.depthRecipe] : []), ...(recipe.sampledRecipe ? [recipe.sampledRecipe] : []),
+ ...(recipe.photometricPriorRecipe ? [recipe.photometricPriorRecipe] : [])];
if (required.some(path => !inputs.some(([candidate]) => candidate === path))) throw new Error('Prepared nebula receipt does not pin every configured source.');
let depthInputs: { recipe: Pin; evidence: Pin } | undefined;
if (recipe.depthRecipe) {
@@ -69,6 +70,18 @@ export async function loadPublishedCompiler(path: string, recipePath: string, fe
const method: unknown = JSON.parse(new TextDecoder().decode(await checkedBytes(result.method, fetchLocal)));
if (!record(method) || method.recipeSha256 !== publication.inputs.find(source => source.path === recipePath)!.sha256 || !Array.isArray(method.implementation))
throw new Error('Prepared nebula method does not match its current recipe.');
+ if (recipe.photometricPriorRecipe) {
+ const model: unknown = JSON.parse(new TextDecoder().decode(inputs.find(([path]) => path === recipe.photometricPriorRecipe)![1]));
+ if (!record(model) || model.schema !== 'cssearth-photometric-mge@1' || model.id !== recipe.id || !record(method.photometricPrior))
+ throw new Error('Prepared nebula omits its configured photometric model.');
+ const evidence = pin(model.evidence), snapshot = pin(method.photometricPrior.recipe), evidenceSnapshot = pin(method.photometricPrior.evidence);
+ if (!evidence.path.startsWith('labs/nebula/models/') ||
+ !publication.inputs.some(input => input.path === evidence.path && input.sha256 === evidence.sha256) ||
+ snapshot.sha256 !== publication.inputs.find(input => input.path === recipe.photometricPriorRecipe)!.sha256 ||
+ evidenceSnapshot.sha256 !== evidence.sha256)
+ throw new Error('Prepared photometric model differs from its configured evidence.');
+ await Promise.all([checkedBytes(snapshot, fetchLocal), checkedBytes(evidenceSnapshot, fetchLocal)]);
+ }
if (recipe.observedStars && (!record(method.observedStars) || !record(method.observedStars.source) ||
method.observedStars.source.path !== recipe.observedStars.path || method.observedStars.source.sha256 !== recipe.observedStars.sha256 ||
!publication.inputs.some(input => input.path === recipe.observedStars!.path && input.sha256 === recipe.observedStars!.sha256)))
diff --git a/labs/nebula/packages/lab/src/features/compiler/model.ts b/labs/nebula/packages/lab/src/features/compiler/model.ts
index 947aabb9db..4fc1306f5d 100644
--- a/labs/nebula/packages/lab/src/features/compiler/model.ts
+++ b/labs/nebula/packages/lab/src/features/compiler/model.ts
@@ -11,7 +11,7 @@ export interface ObservedStarCataloguePin { path: string; sha256: string }
export interface CompilerRequest { action: 'apply'; imageId: 'compiler'; recipePath: string; cataloguePath: string;
imageToFrame: Record; evidence: { sensitivity: number; weights: number[] }; controls: CompilerControls }
export interface CompilerRecipe { schema: 'cssearth-nebula-compiler@1'; id: string; label: string; observationRecipe: string;
- observationCatalogue: string; structureRecipe: string; structureCatalogue: string; jointRecipe?: string; depthRecipe?: string; sampledRecipe?: string;
+ observationCatalogue: string; structureRecipe: string; structureCatalogue: string; jointRecipe?: string; depthRecipe?: string; sampledRecipe?: string; photometricPriorRecipe?: string;
defaultSourceId: string; maximumStars: number; interpretation: string;
defaultControls?: CompilerControls; sourceWeights?: Record; starCatalogue?: CompilerStarCatalogue; observedStars?: ObservedStarCataloguePin; targetControls?: CompilerTargetControls; emissionWindow?: CompilerEmissionWindow }
const finite = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);
@@ -33,11 +33,14 @@ export function readCompilerRecipe(v: unknown): CompilerRecipe {
!jointPath(v.observationRecipe) || !jointPath(v.observationCatalogue) || !jointPath(v.structureRecipe) || !jointPath(v.structureCatalogue) ||
(v.jointRecipe !== undefined && !jointPath(v.jointRecipe)) || (v.depthRecipe !== undefined && (!jointPath(v.depthRecipe) || !v.depthRecipe.startsWith('labs/nebula/models/'))) ||
(v.sampledRecipe !== undefined && (!jointPath(v.sampledRecipe) || !v.sampledRecipe.startsWith('labs/nebula/models/'))) ||
- [v.jointRecipe, v.depthRecipe, v.sampledRecipe].filter(value => value !== undefined).length > 1 || typeof v.defaultSourceId !== 'string' || !range(v.maximumStars, 0, 2000) || !Number.isInteger(v.maximumStars) || typeof v.interpretation !== 'string') throw new TypeError('Invalid compiler recipe.');
+ (v.photometricPriorRecipe !== undefined && (!jointPath(v.photometricPriorRecipe) || !v.photometricPriorRecipe.startsWith('labs/nebula/models/'))) ||
+ [v.jointRecipe, v.depthRecipe, v.sampledRecipe, v.photometricPriorRecipe].filter(value => value !== undefined).length > 1 || typeof v.defaultSourceId !== 'string' || !range(v.maximumStars, 0, 2000) || !Number.isInteger(v.maximumStars) || typeof v.interpretation !== 'string') throw new TypeError('Invalid compiler recipe.');
const targetControls = v.targetControls === undefined ? undefined : readCompilerTargetControls(v.targetControls);
const emissionWindow = v.emissionWindow === undefined ? undefined : readCompilerEmissionWindow(v.emissionWindow);
if (emissionWindow && v.sampledRecipe !== undefined) throw new TypeError('Image emission windows require the emission-field route.');
const controls = v.defaultControls === undefined ? undefined : readCompilerControls(v.defaultControls);
+ if (v.photometricPriorRecipe && controls && controls.depth !== 1)
+ throw new TypeError('Photometric prior controls require depth=1.');
const starCatalogue = v.starCatalogue === undefined ? undefined : readCompilerStarCatalogue(v.starCatalogue);
const observedStars = v.observedStars === undefined ? undefined : readObservedStarCataloguePin(v.observedStars);
if (observedStars && (starCatalogue || v.sampledRecipe)) throw new TypeError('Choose one supported stellar catalogue route.');
@@ -51,7 +54,8 @@ export function readCompilerRecipe(v: unknown): CompilerRecipe {
sourceWeights = Object.fromEntries(Object.entries(v.sourceWeights).map(([id, weight]) => [id, Number(weight)]));
}
return { schema: v.schema, id: v.id, label: v.label, observationRecipe: v.observationRecipe, observationCatalogue: v.observationCatalogue,
- structureRecipe: v.structureRecipe, structureCatalogue: v.structureCatalogue, jointRecipe: v.jointRecipe, ...(v.depthRecipe ? { depthRecipe: v.depthRecipe } : {}), ...(v.sampledRecipe ? { sampledRecipe: v.sampledRecipe } : {}), defaultSourceId: v.defaultSourceId,
+ structureRecipe: v.structureRecipe, structureCatalogue: v.structureCatalogue, jointRecipe: v.jointRecipe, ...(v.depthRecipe ? { depthRecipe: v.depthRecipe } : {}), ...(v.sampledRecipe ? { sampledRecipe: v.sampledRecipe } : {}),
+ ...(v.photometricPriorRecipe ? { photometricPriorRecipe: v.photometricPriorRecipe } : {}), defaultSourceId: v.defaultSourceId,
maximumStars: v.maximumStars, interpretation: v.interpretation,
...(controls ? { defaultControls: controls } : {}), ...(sourceWeights ? { sourceWeights } : {}), ...(starCatalogue ? { starCatalogue } : {}), ...(observedStars ? { observedStars } : {}), ...(targetControls ? { targetControls } : {}), ...(emissionWindow ? { emissionWindow } : {}) };
}
diff --git a/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.css b/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.css
new file mode 100644
index 0000000000..77887b8139
--- /dev/null
+++ b/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.css
@@ -0,0 +1,60 @@
+.lens-radial-legend {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ margin: 10px 0 4px;
+ font-size: 11px;
+ color: var(--text-secondary);
+}
+.lens-radial-key {
+ display: inline-block;
+ width: 14px;
+ height: 2px;
+}
+.lens-radial-legend .lens-radial-key:not(:first-child) {
+ margin-left: 8px;
+}
+.lens-radial-status,
+.lens-radial-note {
+ margin: 6px 0 0;
+ font-size: 10px;
+ line-height: 1.45;
+ color: var(--text-muted);
+ overflow-wrap: anywhere;
+}
+.lens-radial-status[data-error="true"] {
+ color: #e8998c;
+}
+.lens-radial-plot {
+ display: block;
+ width: 100%;
+ height: auto;
+ margin-top: 4px;
+ background: #0b0d11;
+ border-radius: 4px;
+}
+.lens-radial-plot .lens-radial-frame {
+ fill: #0b0d11;
+ stroke: #2a2f3a;
+ stroke-width: 1;
+}
+.lens-radial-plot .lens-radial-grid {
+ stroke: #1d222b;
+ stroke-width: 1;
+}
+.lens-radial-plot .lens-radial-zero {
+ stroke: #5b6472;
+ stroke-width: 1;
+}
+.lens-radial-plot .lens-radial-axis {
+ font-size: 7px;
+ fill: #9aa3b2;
+}
+.lens-radial-numbers {
+ margin: 6px 0 0;
+ font-family: ui-monospace, Menlo, monospace;
+ font-size: 10px;
+ line-height: 1.5;
+ color: #c8cfdb;
+ overflow-wrap: anywhere;
+}
diff --git a/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.test.ts b/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.test.ts
new file mode 100644
index 0000000000..8ef49ded81
--- /dev/null
+++ b/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.test.ts
@@ -0,0 +1,60 @@
+import assert from 'node:assert/strict';
+import { createServer } from 'node:http';
+import test from 'node:test';
+import { build } from 'esbuild';
+import { chromium } from 'playwright';
+
+test('radial panel plots dark bins and replaces the measurement when the lens changes', async () => {
+ const root = process.cwd();
+ const fixture = await build({ stdin: { loader: 'tsx', resolveDir: root, contents: `
+ import { useState } from 'react'; import { createRoot } from 'react-dom/client';
+ import { LensRadialPanel } from './labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel';
+ function App() { const [id, setId] = useState('a'.repeat(64));
+ return <>>; }
+ createRoot(document.getElementById('fixture')).render();` },
+ bundle: true, write: false, format: 'iife', platform: 'browser', outfile: 'fixture.js',
+ loader: { '.css': 'empty' }, jsx: 'automatic' });
+ const script = fixture.outputFiles[0]?.text;
+ assert.ok(script);
+ const server = createServer((request, response) => {
+ const url = new URL(request.url ?? '/', 'http://localhost');
+ if (url.pathname === '/__nebula/reconstruction-radial') {
+ const resultId = url.searchParams.get('resultId');
+ response.setHeader('Content-Type', 'application/json');
+ response.end(JSON.stringify({ schema: 'cssearth-nebula-lens-radial@1', resultId,
+ imageId: resultId?.startsWith('a') ? 'first-lens' : 'second-lens',
+ grid: { width: 10, height: 10 }, bins: 3, footprintPixels: 100, maxRadius: 5,
+ radialBins: [0, 1, 2].map(index => ({ radius: index + 1, pixels: 10,
+ sourceMean: 100, renderMean: index ? 100 : 0, ratio: index ? 1 : 0, signedDelta: index ? 0 : -100 })),
+ halfLightRadiusSource: 2, halfLightRadiusRender: 3, halfLightRadiusRatio: 1.5,
+ rmsLogRatio: 20, worstBin: { radius: 1, ratio: 0, pixels: 10 }, note: 'Test measurement' }));
+ } else {
+ response.setHeader('Content-Type', url.pathname === '/fixture.js' ? 'text/javascript' : 'text/html');
+ response.end(url.pathname === '/fixture.js' ? script : '');
+ }
+ });
+ await new Promise(done => server.listen(0, '127.0.0.1', done));
+ const address = server.address();
+ assert.ok(address && typeof address !== 'string');
+ const browser = await chromium.launch({ headless: true });
+ try {
+ const page = await browser.newPage(), errors: string[] = [];
+ page.on('pageerror', error => errors.push(error.message));
+ await page.goto(`http://127.0.0.1:${address.port}`);
+ for (const id of ['a', 'b']) {
+ await page.waitForSelector(`[data-lens-radial-state="ready"][data-lens-radial-result="${id.repeat(64)}"]`);
+ const lines = page.locator('.lens-radial-plot polyline');
+ assert.equal(await lines.count(), 3);
+ const points = (await lines.nth(2).getAttribute('points'))?.split(' ') ?? [];
+ assert.equal(points.length, 3, 'the zero-ratio core must remain plotted');
+ assert.ok(Number(points[0]?.split(',')[1]) > Number(points[1]?.split(',')[1]), 'the dark core must be below the matched annulus');
+ assert.match(await page.locator('.lens-radial-numbers').innerText(), /worst ×0\.00/);
+ assert.match(await page.locator('.lens-radial-status').innerText(), id === 'a' ? /first-lens/ : /second-lens/);
+ if (id === 'a') await page.getByRole('button', { name: 'Next lens' }).click();
+ }
+ assert.deepEqual(errors, []);
+ } finally {
+ await browser.close();
+ await new Promise(done => server.close(() => done()));
+ }
+});
diff --git a/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.tsx b/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.tsx
new file mode 100644
index 0000000000..6d5eb40c39
--- /dev/null
+++ b/labs/nebula/packages/lab/src/features/reconstruction/lens-radial-panel.tsx
@@ -0,0 +1,108 @@
+/**
+ * Live radial profile for the displayed image lens, drawn as inline SVG: does brightness fall off with radius
+ * the way the source does?
+ *
+ * Levels (a histogram) can match p50/p90/p99 exactly while the structure sits in the wrong place; the
+ * difference map shows where the error is but not its radial shape. This panel plots azimuthally averaged
+ * source and render brightness against radius from the footprint's own centroid, plus their ratio, so a
+ * brightness swap between the centre and an annulus — invisible to a histogram — reads as a curve that
+ * departs from the render÷source = 1 line. Every number comes from `/__nebula/reconstruction-radial`; nothing
+ * is measured here.
+ */
+import { useEffect, useState } from 'react';
+import type { LensRadialBin, LensRadialProfile } from '../../server/services/lens-radial.ts';
+import './lens-radial-panel.css';
+
+const WIDTH = 320, HEIGHT = 150;
+const PLOT = { x: 34, y: 8, w: 280, h: 74 };
+const RATIO = { x: 34, y: 94, w: 280, h: 40 };
+const SOURCE_STROKE = '#c9ced9', RENDER_STROKE = '#48c9c0', RATIO_STROKE = '#e0b34a';
+export const RADIAL_TOOLTIP = 'Radial profile · brightness vs radius, render against this image';
+/** Concentric-rings glyph for the floating Radial profile tool button. */
+export const RadialIcon = () => ;
+
+function polyline(points: string[], stroke: string) {
+ return points.length > 1
+ ? : null;
+}
+
+function RadialPlot({ profile }: { profile: LensRadialProfile }) {
+ const defined = profile.radialBins.filter((bin): bin is LensRadialBin & { sourceMean: number; renderMean: number } =>
+ bin.pixels > 0 && bin.sourceMean !== null && bin.renderMean !== null);
+ const peak = Math.max(1, ...defined.flatMap(bin => [bin.sourceMean, bin.renderMean]));
+ const maxRadius = Math.max(1, profile.maxRadius);
+ const at = (radius: number, value: number, box: typeof PLOT) =>
+ `${(box.x + radius / maxRadius * box.w).toFixed(1)},${(box.y + box.h - Math.max(0, value) / peak * box.h).toFixed(1)}`;
+ const sourceLine = defined.map(bin => at(bin.radius, bin.sourceMean, PLOT));
+ const renderLine = defined.map(bin => at(bin.radius, bin.renderMean, PLOT));
+ const ratioBins = profile.radialBins.filter((bin): bin is LensRadialBin & { ratio: number } => bin.ratio !== null);
+ const logRatio = (ratio: number) => Math.log(Math.max(Number.EPSILON, ratio));
+ const ratioPeak = Math.max(1, ...ratioBins.map(bin => Math.abs(logRatio(bin.ratio))));
+ const ratioLine = ratioBins.map(bin => `${(RATIO.x + bin.radius / maxRadius * RATIO.w).toFixed(1)},` +
+ `${(RATIO.y + RATIO.h / 2 - logRatio(bin.ratio) / ratioPeak * (RATIO.h / 2 - 2)).toFixed(1)}`);
+ return ;
+}
+
+/** Measures while mounted; the floating Radial profile tool mounts it only while its panel is open. */
+export function LensRadialPanel({ resultId }: { resultId?: string }) {
+ const [state, setState] = useState<{ status: 'idle' | 'loading' | 'ready' | 'error'; profile?: LensRadialProfile; error?: string }>({ status: 'idle' });
+ useEffect(() => {
+ if (!resultId) { setState({ status: 'idle' }); return; }
+ const controller = new AbortController();
+ setState({ status: 'loading' });
+ void (async () => {
+ try {
+ const response = await fetch(`/__nebula/reconstruction-radial?resultId=${encodeURIComponent(resultId)}`, { signal: controller.signal });
+ const value = await response.json();
+ if (!response.ok) throw new Error(value.error ?? `Radial profile unavailable (HTTP ${response.status}).`);
+ if (value.schema !== 'cssearth-nebula-lens-radial@1' || value.resultId !== resultId || !Array.isArray(value.radialBins))
+ throw new TypeError('Invalid radial profile measurement.');
+ if (!controller.signal.aborted) setState({ status: 'ready', profile: value as LensRadialProfile });
+ } catch (error) {
+ if (!controller.signal.aborted) setState({ status: 'error', error: error instanceof Error ? error.message : String(error) });
+ }
+ })();
+ return () => controller.abort();
+ }, [resultId]);
+ const profile = state.profile;
+ return
+ {state.status === 'error' &&
{state.error}
}
+ {state.status === 'loading' &&
Measuring this lens…
}
+ {profile && <>
+
+ source image
+ render
+ ratio
+
+
+
+ half-light radius {profile.halfLightRadiusSource?.toFixed(1) ?? '—'}px → {profile.halfLightRadiusRender?.toFixed(1) ?? '—'}px
+ {' '}(×{profile.halfLightRadiusRatio?.toFixed(2) ?? '—'})
+ rms log ratio {profile.rmsLogRatio?.toFixed(2) ?? '—'} ·
+ {' '}worst {profile.worstBin ? `×${profile.worstBin.ratio.toFixed(2)} @${profile.worstBin.radius.toFixed(0)}px` : '—'}
+
+
+ {profile.imageId} · grid {profile.grid.width}×{profile.grid.height} · footprint {profile.footprintPixels.toLocaleString()} px ·
+ {' '}{profile.bins} bins to {profile.maxRadius.toFixed(0)}px
+
+
{profile.note}
+ >}
+
;
+}
diff --git a/labs/nebula/packages/lab/src/features/reconstruction/reconstruction-controls-react.tsx b/labs/nebula/packages/lab/src/features/reconstruction/reconstruction-controls-react.tsx
index 4b5c6d91cb..ec2672d752 100644
--- a/labs/nebula/packages/lab/src/features/reconstruction/reconstruction-controls-react.tsx
+++ b/labs/nebula/packages/lab/src/features/reconstruction/reconstruction-controls-react.tsx
@@ -10,6 +10,7 @@ import { readCloudAppearance, saveCloudAppearance } from './cloud-appearance-sto
import { CloudAppearanceControls } from './cloud-appearance-controls';
import { LensLevelsPanel, LevelsIcon, LEVELS_TOOLTIP } from './lens-levels-panel';
import { differenceTool } from './difference-map';
+import { LensRadialPanel, RadialIcon, RADIAL_TOOLTIP } from './lens-radial-panel';
import type { DifferenceOverlayState } from '../legacy-viewer/difference-plane';
import { saveLensSettings } from './lens-settings-export.ts';
import { ImageCredit } from '../workspace/image-credit';
@@ -276,10 +277,12 @@ export function ReconstructionControls({ context, viewerBusy: busy, onSelect, ca
{view.appearanceDirty && !view.running && !view.error && !view.processDisabled ? 'Changes ready · Preview to apply.' : view.text}
- {/* Levels and the difference map need a source image: no tools for the unpainted density or an unbaked lens. */}
+ {/* Levels, the difference map and the radial profile need a source image: no tools for the unpainted density or an unbaked lens. */}
,
panel: },
- ...differenceTool(view.lensResultId, difference, onDifference)] : []} />
+ ...differenceTool(view.lensResultId, difference, onDifference),
+ { id: 'radial', label: 'Radial profile', tooltip: RADIAL_TOOLTIP, icon: ,
+ panel: }] : []} />
diff --git a/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.test.ts b/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.test.ts
index ba6b14f7f8..dcad12bde2 100644
--- a/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.test.ts
+++ b/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.test.ts
@@ -5,7 +5,7 @@ import { shapeCloudCamera } from '../../adapters/viewer/shape-cloud-camera';
import { preparedVolumeCameraTransform } from '../../adapters/viewer/camera-reference';
import type { PreparedCssVolume } from '../../adapters/viewer/camera-reference';
-const frame: PreparedCssVolume['frame'] = { referenceFrame: 'lab', epochJdTt: 2451545, originM: [0, 0, 0],
+const frame: PreparedCssVolume['frame'] = { referenceFrame: 'lab-sky-west-north-toward', epochJdTt: 2451545, originM: [0, 0, 0],
localToReferenceXyzw: [0, 0, 0, 1], metersPerUnit: 1, boundsUnits: { min: [-5, -5, -5], max: [5, 5, 5] } };
const image = { width: 768, height: 534, unitsPerPixel: 10 / 768 };
test('flat photo corners follow the actual cloud camera through compound yaw and pitch', () => {
@@ -29,8 +29,16 @@ test('triad uses source axes, registered screen orientation and the correct Eart
assert.deepEqual(earth[0]!.screen, [1, 0]); assert.deepEqual(earth[1]!.screen, [0, 1]);
assert.equal(Math.hypot(...earth[2]!.screen), 0); assert.equal(earth[2]!.towardEye, 1);
const camera = shapeCloudCamera(frame, image, image, { zoom: 1, panX: 0, panY: 0 }, 0, 0);
- assert.ok(camera.world.pose.positionM[2] < 0, 'Earth-view camera must lie on physical −Z.');
+ assert.ok(camera.world.pose.positionM[2] > 0, 'Earth-view camera must lie on prepared physical +Z (toward).');
const back = shapeCloudImageAxes(180, 0, [1, 0, 0, 1, 0, 0]); assert.equal(back[2]!.towardEye, -1);
const registered = shapeCloudImageAxes(0, 0, [0, 2, -2, 0, 100, 60]);
assert.deepEqual(registered[0]!.screen, [0, 1]); assert.deepEqual(registered[1]!.screen, [-1, 0]);
+ for (const [yaw, pitch] of [[0, 0], [35, 27], [90, -30], [-52, -48]]) {
+ const axes = shapeCloudImageAxes(yaw!, pitch!, [1, 0, 0, 1, 0, 0]);
+ const posed = shapeCloudCamera(frame, image, image, { zoom: 1, panX: 0, panY: 0 }, yaw!, pitch!);
+ const { rotation: r } = preparedVolumeCameraTransform(posed, frame);
+ // Physical [X, -Y, +Z] maps to the retained renderer's swapped [Y, X, Z] basis.
+ const actual = [[r[1]!, r[4]!, r[7]!], [-r[0]!, -r[3]!, -r[6]!], [r[2]!, r[5]!, r[8]!]];
+ for (let i = 0; i < 3; i++) assert.ok(Math.hypot(...[...axes[i]!.screen, axes[i]!.towardEye].map((n, j) => n - actual[i]![j]!)) < 1e-10);
+ }
});
diff --git a/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.ts b/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.ts
index e717e5ad23..d54d20e36b 100644
--- a/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.ts
+++ b/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud-photo-pose.ts
@@ -9,7 +9,7 @@ export function shapeCloudPhotoPose(width: number, height: number, yawDegrees: n
return [a, b, 0, d, width / 2 * (1 - a), height / 2 * (1 - d) - b * width / 2];
}
export interface ImageAxis { id: 'x' | 'y' | 'earth'; label: string; color: string; screen: [number, number]; towardEye: number }
-/** Image X is physical +X, Image Y is physical −Y, and toward Earth is physical −Z at the Earth pose. */
+/** Image X is physical +X, Image Y is physical −Y, and toward Earth is prepared physical +Z. */
export function shapeCloudImageAxes(yawDegrees: number, pitchDegrees: number, registration: Matrix): ImageAxis[] {
const yaw = yawDegrees * Math.PI / 180, pitch = pitchDegrees * Math.PI / 180;
const cy = Math.cos(yaw), sy = Math.sin(yaw), cx = Math.cos(pitch), sx = Math.sin(pitch);
diff --git a/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud.css b/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud.css
index b188281a7a..7e16d86699 100644
--- a/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud.css
+++ b/labs/nebula/packages/lab/src/features/shape-cloud/shape-cloud.css
@@ -35,7 +35,7 @@
.shape-cloud-source-slot[hidden] { display: none; }
.shape-cloud-pane { position: relative; width: 100%; height: 100%; overflow: hidden; }
.shape-cloud-pane-label { position: absolute; top: 14px; left: 50%; transform: translateX(-50%); z-index: 2; color: #b5bac1; font-size: 11px; white-space: nowrap; pointer-events: none; }
-.shape-cloud-viewport { position: absolute; inset: 38px 0 0; overflow: hidden; touch-action: none; cursor: grab; }
+.shape-cloud-viewport { position: absolute; inset: 38px 0 0; overflow: hidden; transform: translateZ(0); touch-action: none; cursor: grab; }
.shape-cloud-viewport:active { cursor: grabbing; }
.shape-cloud-frame, .shape-cloud-image-plane { position: absolute; top: 0; left: 0; transform-origin: 0 0; }
.shape-cloud-source, .shape-cloud-render-host, .shape-cloud-guides { position: absolute; top: 0; left: 0; }
diff --git a/labs/nebula/packages/lab/src/resources/model-catalogue.test.ts b/labs/nebula/packages/lab/src/resources/model-catalogue.test.ts
index 620bb41040..012d5cfd90 100644
--- a/labs/nebula/packages/lab/src/resources/model-catalogue.test.ts
+++ b/labs/nebula/packages/lab/src/resources/model-catalogue.test.ts
@@ -11,7 +11,7 @@ const digest = (bytes: Buffer) => createHash('sha256').update(bytes).digest('hex
test('the curated lab retains the selected LMC images, SMC comparisons and verified density banks', async () => {
const ids = ['vista-infrared', 'horalek-widefield', 'wise-wide-infrared'];
const subjects = await read('labs/nebula/packages/lab/src/state/subjects.json');
- assert.deepEqual(subjects.map((subject: {id: string}) => subject.id), ['lmc-clouds', 'smc-particles', 'smc-vmc', 'smc-ellipsoid', 'smc-constrained', 'm2-9-inferred', 'helix-single-axis', 'helix-model-prior', 'm42', 'm8', 'carina', 'ngc6357', 'm78', 'horsehead', 'm45', 'm1']);
+ assert.deepEqual(subjects.map((subject: {id: string}) => subject.id), ['lmc-clouds', 'smc-particles', 'smc-vmc', 'smc-ellipsoid', 'smc-constrained', 'm2-9-inferred', 'helix-single-axis', 'helix-model-prior', 'm42', 'm8', 'carina', 'ngc6357', 'm78', 'horsehead', 'm45', 'omega-centauri', 'm1']);
const experiment = subjects.find((subject: {id: string}) => subject.id === 'm2-9-inferred');
assert.equal(experiment.density, undefined, 'An inferred-emission experiment must not masquerade as an independent density prior.');
assert.equal(experiment.directory, experiment.emissionExperiment.directory);
diff --git a/labs/nebula/packages/lab/src/server/services/density-reconstruction.ts b/labs/nebula/packages/lab/src/server/services/density-reconstruction.ts
index 2d8e3bf750..4393d500a4 100644
--- a/labs/nebula/packages/lab/src/server/services/density-reconstruction.ts
+++ b/labs/nebula/packages/lab/src/server/services/density-reconstruction.ts
@@ -19,6 +19,7 @@ import { lensSettingsHandler } from '../routes/lens-settings.ts';
import { discoverFiniteLensBundle, finiteModelStarsPath } from './finite-lens-bundles.ts';
import { lensLevels } from './lens-levels.ts';
import { lensDifferenceHandler } from './lens-difference.ts';
+import { lensRadialHandler } from './lens-radial.ts';
const hash = (bytes: Buffer | string) => createHash('sha256').update(bytes).digest('hex');
const cache = '.local/nebula-lab/reconstructions';
@@ -285,6 +286,8 @@ export function reconstructionPlugin(root: string): Plugin {
});
// Read-only difference map for one saved lens: `format=png` is the overlay image, otherwise its legend JSON.
server.middlewares.use('/__nebula/reconstruction-difference', lensDifferenceHandler(root, id => readPreparedReconstruction(root, id)));
+ // Read-only azimuthal radial profile for one saved lens: does brightness fall off with radius like the source?
+ server.middlewares.use('/__nebula/reconstruction-radial', lensRadialHandler(root, id => readPreparedReconstruction(root, id)));
server.middlewares.use('/__nebula/reconstruction', async (request, response) => {
try {
if (request.method !== 'GET') throw new TypeError('Use Preview to start a reconstruction.');
diff --git a/labs/nebula/packages/lab/src/server/services/lens-radial.test.ts b/labs/nebula/packages/lab/src/server/services/lens-radial.test.ts
new file mode 100644
index 0000000000..008d2a52c4
--- /dev/null
+++ b/labs/nebula/packages/lab/src/server/services/lens-radial.test.ts
@@ -0,0 +1,193 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import sharp from 'sharp';
+import { createHash } from 'node:crypto';
+import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import { dirname, join } from 'node:path';
+import type { IncomingMessage, ServerResponse } from 'node:http';
+import { RADIAL_BINS, lensRadialProfile, lensRadialHandler, radialProfileStatistics } from './lens-radial.ts';
+import { lensLevelStatistics, type LensLevelGrid } from './lens-levels.ts';
+import type { PreparedReconstruction } from '../../features/reconstruction/reconstruction-types.ts';
+
+const SIZE = 41, CENTER = (SIZE - 1) / 2, WIDTH = SIZE, HEIGHT = SIZE, PIXELS = WIDTH * HEIGHT;
+const radiusAt = (index: number) => { const x = index % WIDTH, y = Math.floor(index / WIDTH); return Math.hypot(x - CENTER, y - CENTER); };
+/** A centrally concentrated brightness profile, like the branch's subject: bright core, smooth falloff. Always
+ * positive over this 41x41 domain (minimum ~22 at the corners, radius <= 28.3), so the render's chromaticity
+ * normalisation (level * raw/peak) never hits the "no colour information" zero-clamp — the one thing that would
+ * make a raw pixel value, rather than the deliberate render target below, decide what gets delivered. */
+const profile = (r: number) => Math.round(220 - 7 * r);
+const sourceValues = Array.from({ length: PIXELS }, (_, p) => profile(radiusAt(p)));
+/** The 5th-percentile sky pedestal `lensLevelPairs` computes from these same raw values (`quantile`, ./lens-levels.ts). */
+const SKY = [...sourceValues].sort((a, b) => a - b)[Math.floor(PIXELS * .05)]!;
+const sourceLevelAt = (p: number) => Math.max(0, sourceValues[p]! - SKY);
+const levelAtRadius = (r: number) => Math.max(0, profile(r) - SKY);
+
+/**
+ * A grid whose grey source is the profile above, and whose delivered render level at each pixel is exactly
+ * `targetRenderLevel(p)` (grey source ⇒ chromaticity is 1, so `untonedRender` reduces to the projection byte
+ * unchanged). This lets a control be defined directly in the same "source level" space `lensLevelPairs` reports,
+ * with no incidental sky-pedestal or clamp interaction to reason about.
+ */
+function buildGrid(targetRenderLevel: (p: number) => number): LensLevelGrid {
+ const projection = new Uint8Array(PIXELS), source = new Float64Array(PIXELS * 3), mask = new Uint8Array(PIXELS).fill(1);
+ for (let p = 0; p < PIXELS; p++) {
+ source.fill(sourceValues[p]!, p * 3, p * 3 + 3);
+ projection[p] = Math.min(255, Math.max(0, Math.round(targetRenderLevel(p))));
+ }
+ return { width: WIDTH, height: HEIGHT, projection, source, mask };
+}
+const material = { channelGain: null, toneCurve: null };
+const identity = (p: number) => sourceLevelAt(p);
+/** Reverses radius rank: the pixel at rank i (sorted by radius ascending) renders the source LEVEL of rank
+ * N-1-i. This is a bijection on the pixel domain over the exact array `lensLevelStatistics` histograms, so the
+ * multiset of rendered levels is EXACTLY the multiset of source levels — the histogram cannot tell the two
+ * apart — while spatially the profile is inverted: brightness moves from the centre to the rim. */
+const radiusOrder = Array.from({ length: PIXELS }, (_, p) => p).sort((a, b) => radiusAt(a) - radiusAt(b) || a - b);
+const sourceLevelByRank = radiusOrder.map(p => sourceLevelAt(p));
+const reversedTarget = new Array(PIXELS);
+radiusOrder.forEach((p, rank) => { reversedTarget[p] = sourceLevelByRank[sourceLevelByRank.length - 1 - rank]!; });
+const reversedByRadius = (p: number) => reversedTarget[p]!;
+/** Radially compressed by factor `k`: at a fixed radius the render reads like the source read at k·r, so k>1 falls off faster. */
+const compressedBy = (k: number) => (p: number) => levelAtRadius(radiusAt(p) * k);
+
+test('a render that matches its source at every radius reports agreement, not just an overall match', () => {
+ const stats = radialProfileStatistics(buildGrid(identity), material);
+ assert.equal(stats.footprintPixels, PIXELS);
+ assert.deepEqual(stats.centroid, { x: CENTER, y: CENTER });
+ const defined = stats.radialBins.filter(bin => bin.ratio !== null);
+ assert.ok(defined.length > RADIAL_BINS / 2, `too few defined bins: ${defined.length}`);
+ for (const bin of defined) assert.ok(Math.abs(bin.ratio! - 1) < .1, `bin @${bin.radius.toFixed(1)} ratio ${bin.ratio}`);
+ assert.ok(stats.rmsLogRatio! < .1, `rmsLogRatio ${stats.rmsLogRatio}`);
+ assert.ok(Math.abs(stats.halfLightRadiusRatio! - 1) < .1, `half-light ratio ${stats.halfLightRadiusRatio}`);
+});
+
+test('a centre <-> annulus brightness swap conserves the histogram exactly, and levels cannot see it, but the radial profile flags it', () => {
+ const grid = buildGrid(reversedByRadius);
+ // Proof the swap is histogram-blind: the delivered render levels are a permutation of the delivered source levels.
+ const levels = lensLevelStatistics(grid, material);
+ for (const channel of levels.channels) {
+ // The permutation conserves the exact multiset of levels, so every histogram-derived Levels number reads
+ // as a perfect match: identical bins, identical percentiles, identical worst bin, zero net bias.
+ assert.deepEqual([...channel.sourceHistogram], [...channel.renderHistogram], 'the two histograms must be identical bin-for-bin');
+ assert.ok(Math.abs(channel.p50Ratio - 1) < .01, `Levels p50 ratio should read matched, got ${channel.p50Ratio}`);
+ assert.ok(Math.abs(channel.p90Ratio - 1) < .01, `Levels p90 ratio should read matched, got ${channel.p90Ratio}`);
+ assert.equal(channel.worstBin.pixels, 0, `Levels' own worst-bin count should read 0, got ${channel.worstBin.pixels}`);
+ assert.ok(Math.abs(channel.signedMeanDelta) < .5, `Levels net bias should read ~0, got ${channel.signedMeanDelta}`);
+ }
+ const stats = radialProfileStatistics(grid, material);
+ const defined = stats.radialBins.filter(bin => bin.ratio !== null);
+ const core = defined[0]!, rim = defined.at(-1)!;
+ // The outermost bins clamp to a zero source mean (the bottom 5% the sky pedestal removes), so their ratio is
+ // null by design (a ratio against ~0 is meaningless) — inspect the outermost bin that still has a defined one.
+ assert.ok(core.ratio !== null && core.ratio < .6, `core ratio should read far below 1, got ${core.ratio}`);
+ assert.ok(rim.ratio !== null && rim.ratio > 1.5, `outer ratio should read far above 1, got ${rim.ratio}`);
+ assert.ok(stats.rmsLogRatio! > .3, `rmsLogRatio should be large, got ${stats.rmsLogRatio}`);
+ assert.ok(stats.worstBin !== null && Math.abs(Math.log(stats.worstBin.ratio)) > .3, JSON.stringify(stats.worstBin));
+ // The permutation also shows up as a pushed-out half-light radius: flux arrives late because it was moved outward.
+ assert.ok(stats.halfLightRadiusRatio! > 1.2, `half-light ratio should read pushed out, got ${stats.halfLightRadiusRatio}`);
+});
+
+test('a radially compressed render is caught by the half-light radius even though no single bin is wildly wrong', () => {
+ const matched = radialProfileStatistics(buildGrid(identity), material);
+ const compressed = radialProfileStatistics(buildGrid(compressedBy(1.4)), material);
+ assert.ok(Math.abs(matched.halfLightRadiusRatio! - 1) < .1);
+ assert.ok(compressed.halfLightRadiusRatio! < .85, `compressed half-light ratio ${compressed.halfLightRadiusRatio}`);
+ assert.ok(compressed.halfLightRadiusRatio! > 0, 'half-light ratio must stay positive');
+ // A milder compression reads as a milder shift, monotone in the compression factor.
+ const milder = radialProfileStatistics(buildGrid(compressedBy(1.15)), material);
+ assert.ok(milder.halfLightRadiusRatio! > compressed.halfLightRadiusRatio! && milder.halfLightRadiusRatio! < 1,
+ `milder ${milder.halfLightRadiusRatio} vs compressed ${compressed.halfLightRadiusRatio}`);
+});
+
+test('a missing bright core remains the worst radial defect even when its render is exactly zero', () => {
+ const stats = radialProfileStatistics(buildGrid(p => radiusAt(p) < 8 ? 0 : identity(p)), material);
+ assert.ok(stats.radialBins.some(bin => bin.ratio === 0));
+ assert.equal(stats.worstBin?.ratio, 0);
+ assert.ok(stats.rmsLogRatio !== null && Number.isFinite(stats.rmsLogRatio) && stats.rmsLogRatio > 1);
+ const dark = radialProfileStatistics(buildGrid(() => 0), material);
+ assert.equal(dark.worstBin?.ratio, 0);
+ assert.ok(dark.rmsLogRatio !== null && Number.isFinite(dark.rmsLogRatio) && dark.rmsLogRatio > 1);
+ assert.equal(dark.halfLightRadiusRender, null);
+});
+
+test('the footprint mask is respected, and empty or degenerate bins report null rather than NaN', () => {
+ // A small disk footprint inside the same field, with many more bins than the disk has room for at large radius.
+ const disk = buildGrid(identity);
+ const radius = 10;
+ for (let p = 0; p < PIXELS; p++) disk.mask[p] = radiusAt(p) <= radius ? 1 : 0;
+ const stats = radialProfileStatistics(disk, material, 40);
+ assert.ok(stats.footprintPixels < PIXELS, 'the mask must shrink the footprint');
+ const empty = stats.radialBins.filter(bin => bin.pixels === 0);
+ assert.ok(empty.length > 0, 'a 40-bin profile of a radius-10 disk must have some empty outer bins');
+ for (const bin of empty) assert.deepEqual([bin.sourceMean, bin.renderMean, bin.ratio, bin.signedDelta], [null, null, null, null]);
+ assert.doesNotMatch(JSON.stringify(stats), /NaN|Infinity/, 'no NaN/Infinity may leak into the JSON-serialisable result');
+ // Pixels outside the mask must not move the result: changing their values must not change any statistic.
+ const untouched = buildGrid(identity);
+ for (let p = 0; p < PIXELS; p++) untouched.mask[p] = radiusAt(p) <= radius ? 1 : 0;
+ for (let p = 0; p < PIXELS; p++) if (!untouched.mask[p]) { untouched.projection[p] = 255; untouched.source.fill(0, p * 3, p * 3 + 3); }
+ assert.deepEqual(radialProfileStatistics(untouched, material, 40), stats);
+});
+
+const digest = (bytes: Buffer) => createHash('sha256').update(bytes).digest('hex');
+/** A saved lens in the file shape `bakeFiniteLens` publishes, reused from the difference-map fixture pattern. */
+async function fixture() {
+ const root = await mkdtemp(join(tmpdir(), 'nebula-lens-radial-'));
+ const resultId = 'e'.repeat(64), directory = `.local/nebula-lab/reconstructions/${resultId}`;
+ const width = 32, height = 24, bounds = { min: [-4, -3], max: [4, 3] };
+ const artifacts: Record = {};
+ const save = async (path: string, bytes: Buffer) => {
+ await mkdir(dirname(join(root, directory, path)), { recursive: true }); await writeFile(join(root, directory, path), bytes);
+ artifacts[path] = { sha256: digest(bytes), bytes: bytes.length };
+ };
+ const cx = (width - 1) / 2, cy = (height - 1) / 2;
+ const grey = (i: number, j: number) => Math.max(0, Math.round(200 - 9 * Math.hypot(i - cx, j - cy)));
+ const projection = Buffer.alloc(width * height), rgb = Buffer.alloc(width * height * 3);
+ for (let j = 0; j < height; j++) for (let i = 0; i < width; i++) {
+ const value = grey(i, j); projection[j * width + i] = value; rgb.fill(value, (j * width + i) * 3, (j * width + i) * 3 + 3);
+ }
+ await save('source/fit-projection.png', await sharp(projection, { raw: { width, height, channels: 1 } }).png().toBuffer());
+ await save('source/registered-image.png', await sharp(rgb, { raw: { width, height, channels: 3 } }).png().toBuffer());
+ await save('source/original-image.png', await sharp(Buffer.alloc(width * height * 4, 255), { raw: { width, height, channels: 4 } }).png().toBuffer());
+ await save('source/provenance.json', Buffer.from(JSON.stringify({ densityProjection: { width, height, tangentBoundsKpc: bounds },
+ sourceRegistration: { tangentBoundsKpc: bounds } })));
+ await writeFile(join(root, directory, 'manifest.json'), JSON.stringify({
+ schema: 'cssearth-nebula-reconstruction-artifacts@1', id: `reconstruction-${resultId}`, artifacts }));
+ const prepared = { schema: 'cssearth-nebula-reconstruction@1', resultId, imageId: 'test-lens', subject: { id: `reconstruction-${resultId}`, directory },
+ finiteMaterial: { modelResultId: 'b'.repeat(64), sourceResultId: 'c'.repeat(64) } } as unknown as PreparedReconstruction;
+ return { root, prepared };
+}
+async function get(handler: ReturnType, url: string, method = 'GET') {
+ const headers: Record = {}, chunks: Buffer[] = [];
+ const response = { statusCode: 200, setHeader(name: string, value: string) { headers[name.toLowerCase()] = value; },
+ end(body?: string | Buffer) { if (body !== undefined) chunks.push(Buffer.from(body)); } };
+ await handler({ method, url } as IncomingMessage, response as unknown as ServerResponse);
+ return { status: response.statusCode, headers, body: Buffer.concat(chunks) };
+}
+
+test('the route answers the radial profile JSON for a saved lens whose render matches its own source', async () => {
+ const { root, prepared } = await fixture();
+ try {
+ const handler = lensRadialHandler(root, async id => { assert.equal(id, prepared.resultId); return prepared; });
+ const response = await get(handler, `/?resultId=${prepared.resultId}`);
+ assert.equal(response.status, 200); assert.equal(response.headers['content-type'], 'application/json');
+ const value = JSON.parse(response.body.toString());
+ assert.equal(value.schema, 'cssearth-nebula-lens-radial@1');
+ assert.equal(value.resultId, prepared.resultId);
+ assert.ok(Array.isArray(value.radialBins) && value.radialBins.length === RADIAL_BINS);
+ assert.ok(Math.abs(value.halfLightRadiusRatio - 1) < .2, JSON.stringify(value.halfLightRadiusRatio));
+ const direct = await lensRadialProfile(root, prepared);
+ assert.deepEqual(JSON.parse(JSON.stringify(direct)), value);
+ } finally { await rm(root, { recursive: true, force: true }); }
+});
+
+test('the route refuses writes, bad identities and non-lens results', async () => {
+ const { root, prepared } = await fixture();
+ try {
+ const handler = lensRadialHandler(root, async () => prepared);
+ assert.equal((await get(handler, `/?resultId=${prepared.resultId}`, 'POST')).status, 400);
+ assert.match((await get(handler, '/?resultId=nope')).body.toString(), /Invalid reconstruction identity/);
+ const density = lensRadialHandler(root, async () => ({ ...prepared, finiteMaterial: undefined }) as PreparedReconstruction);
+ assert.match((await get(density, `/?resultId=${prepared.resultId}`)).body.toString(), /baked image lens/);
+ } finally { await rm(root, { recursive: true, force: true }); }
+});
diff --git a/labs/nebula/packages/lab/src/server/services/lens-radial.ts b/labs/nebula/packages/lab/src/server/services/lens-radial.ts
new file mode 100644
index 0000000000..ca1290ecd8
--- /dev/null
+++ b/labs/nebula/packages/lab/src/server/services/lens-radial.ts
@@ -0,0 +1,138 @@
+/**
+ * Does brightness fall off with radius the way the source does? A histogram (Levels) is spatially blind: it
+ * can match p50/p90/p99 exactly while the structure sits in the wrong place. The difference map shows where
+ * the error is but does not quantify its radial shape. This measures the azimuthally averaged source and
+ * render brightness in radial bins from the footprint's own centroid, so a brightness swap between the centre
+ * and an annulus - invisible to the histogram, since it only rearranges where the same values sit - shows up
+ * as a per-bin ratio far from one.
+ *
+ * Reuses the same pinned grid, source/render pairs and material corrections `lens-levels.ts` and
+ * `lens-difference.ts` read (`lensLevelPairs`, `loadLensLevelGrid`), so the three diagnostics can never
+ * disagree about what "source" and "render" mean.
+ */
+import type { IncomingMessage, ServerResponse } from 'node:http';
+import type { PreparedReconstruction } from '../../features/reconstruction/reconstruction-types.ts';
+import { lensLevelPairs, loadLensLevelGrid, type LensLevelGrid, type LensLevelMaterial } from './lens-levels.ts';
+
+/** Radial bins from the footprint centroid out to the farthest covered pixel. */
+export const RADIAL_BINS = 24;
+/** Rec. 709 luma weights, applied to the 8-bit channel levels (same weights the difference map uses). */
+const LUMA = [.2126, .7152, .0722] as const;
+
+export interface LensRadialBin {
+ /** Bin centre radius, in grid pixels from the footprint centroid. */
+ radius: number;
+ pixels: number;
+ /** Null when the bin holds no covered pixels. */
+ sourceMean: number | null; renderMean: number | null;
+ /** Null when the source side of the bin averages to (near) zero, so a ratio would be meaningless. */
+ ratio: number | null;
+ signedDelta: number | null;
+}
+export interface LensRadialWorstBin { radius: number; ratio: number; pixels: number }
+export interface LensRadialProfile {
+ schema: 'cssearth-nebula-lens-radial@1';
+ resultId: string; imageId: string;
+ grid: { width: number; height: number };
+ bins: number;
+ footprintPixels: number;
+ /** The footprint's own geometric centroid, in grid pixel coordinates. Never brightness-weighted. */
+ centroid: { x: number; y: number };
+ maxRadius: number; binWidth: number;
+ radialBins: LensRadialBin[];
+ /** Radius enclosing half the total luminance, source and render separately; null with no positive flux. */
+ halfLightRadiusSource: number | null; halfLightRadiusRender: number | null;
+ /** Render half-light radius over source: 1 matched, below 1 render compressed toward the centre, above 1 expanded. */
+ halfLightRadiusRatio: number | null;
+ /** Pixel-count-weighted RMS of |log(ratio)|; ratios below machine epsilon use that finite floor. */
+ rmsLogRatio: number | null;
+ /** The bin whose ratio departs furthest from one. Null when no bin has a defined ratio. */
+ worstBin: LensRadialWorstBin | null;
+ note: string;
+}
+
+/** The whole measurement, over one prepared grid. Kept pure so its tests never need a bake. */
+export function radialProfileStatistics(grid: LensLevelGrid, material: LensLevelMaterial = { channelGain: null, toneCurve: null },
+ bins = RADIAL_BINS): Pick {
+ if (!Number.isInteger(bins) || bins < 1) throw new TypeError('The radial profile needs at least one bin.');
+ const { width } = grid;
+ const pairs = lensLevelPairs(grid, material);
+ const footprint = pairs.covered.length;
+ if (!footprint) throw new TypeError('This lens image covers none of the model projection.');
+ let sx = 0, sy = 0;
+ for (const p of pairs.covered) { sx += p % width; sy += Math.floor(p / width); }
+ const centroid = { x: sx / footprint, y: sy / footprint };
+ const radii = new Float64Array(footprint), sourceLum = new Float64Array(footprint), renderLum = new Float64Array(footprint);
+ let maxRadius = 0;
+ pairs.covered.forEach((p, k) => {
+ const x = p % width, y = Math.floor(p / width), r = Math.hypot(x - centroid.x, y - centroid.y);
+ radii[k] = r; if (r > maxRadius) maxRadius = r;
+ let source = 0, render = 0;
+ for (let c = 0; c < 3; c++) { source += LUMA[c] * pairs.source[k * 3 + c]!; render += LUMA[c] * pairs.render[k * 3 + c]!; }
+ sourceLum[k] = source; renderLum[k] = render;
+ });
+ const binWidth = maxRadius > 0 ? maxRadius / bins : 1;
+ const sourceSum = new Float64Array(bins), renderSum = new Float64Array(bins), counts = new Int32Array(bins);
+ for (let k = 0; k < footprint; k++) {
+ const index = Math.min(bins - 1, Math.floor(radii[k]! / binWidth));
+ sourceSum[index]! += sourceLum[k]!; renderSum[index]! += renderLum[k]!; counts[index]! += 1;
+ }
+ const radialBins: LensRadialBin[] = [];
+ let worstBin: LensRadialWorstBin | null = null, worstDeviation = 0, weightedSquare = 0, weight = 0;
+ for (let i = 0; i < bins; i++) {
+ const pixels = counts[i]!, radius = (i + .5) * binWidth;
+ if (!pixels) { radialBins.push({ radius, pixels: 0, sourceMean: null, renderMean: null, ratio: null, signedDelta: null }); continue; }
+ const sourceMean = sourceSum[i]! / pixels, renderMean = renderSum[i]! / pixels, signedDelta = renderMean - sourceMean;
+ const ratio = sourceMean > 1e-6 ? renderMean / sourceMean : null;
+ radialBins.push({ radius, pixels, sourceMean, renderMean, ratio, signedDelta });
+ if (ratio !== null) {
+ // A completely missing bright bin is a maximal defect, not missing evidence. Keep JSON finite.
+ const deviation = Math.abs(Math.log(Math.max(Number.EPSILON, ratio)));
+ weightedSquare += deviation * deviation * pixels; weight += pixels;
+ if (deviation > worstDeviation) { worstDeviation = deviation; worstBin = { radius, ratio, pixels }; }
+ }
+ }
+ const halfLight = (sums: Float64Array) => {
+ let total = 0; for (const value of sums) total += value;
+ if (!(total > 0)) return null;
+ let cumulative = 0;
+ for (let i = 0; i < bins; i++) { cumulative += sums[i]!; if (cumulative >= total / 2) return (i + .5) * binWidth; }
+ return maxRadius;
+ };
+ const halfLightRadiusSource = halfLight(sourceSum), halfLightRadiusRender = halfLight(renderSum);
+ const halfLightRadiusRatio = halfLightRadiusSource !== null && halfLightRadiusRender !== null && halfLightRadiusSource > 0
+ ? halfLightRadiusRender / halfLightRadiusSource : null;
+ return { footprintPixels: footprint, centroid, maxRadius, binWidth, radialBins,
+ halfLightRadiusSource, halfLightRadiusRender, halfLightRadiusRatio,
+ rmsLogRatio: weight > 0 ? Math.sqrt(weightedSquare / weight) : null, worstBin };
+}
+
+/** Measure one saved finite lens, from the same pinned grid the levels and difference routes read. */
+export async function lensRadialProfile(root: string, prepared: PreparedReconstruction, options: { bins?: number } = {}): Promise {
+ const { grid, material } = await loadLensLevelGrid(root, prepared);
+ const bins = options.bins ?? RADIAL_BINS;
+ const statistics = radialProfileStatistics(grid, material, bins);
+ return { schema: 'cssearth-nebula-lens-radial@1', resultId: prepared.resultId, imageId: prepared.imageId,
+ grid: { width: grid.width, height: grid.height }, bins, ...statistics,
+ note: 'Azimuthally averaged luminance in radial bins from the footprint’s own geometric centroid. Levels ' +
+ 'can match p50/p90/p99 while the structure sits in the wrong place; a per-bin ratio far from one, or a ' +
+ 'half-light radius ratio far from one, means the render’s radial shape differs even where its histogram does not. ' +
+ 'Log ratios use machine epsilon as a finite floor for a completely dark render bin.' };
+}
+
+/** GET `?resultId=` answers the radial profile JSON. Never writes. */
+export function lensRadialHandler(root: string, read: (resultId: string) => Promise) {
+ return async (request: IncomingMessage, response: ServerResponse) => {
+ try {
+ if (request.method !== 'GET') throw new TypeError('The radial profile is read-only.');
+ const url = new URL(request.url ?? '/', 'http://localhost'), id = url.searchParams.get('resultId') ?? '';
+ if (!/^[a-f0-9]{64}$/.test(id)) throw new TypeError('Invalid reconstruction identity.');
+ const value = await lensRadialProfile(root, await read(id));
+ response.setHeader('Cache-Control', 'no-store'); response.setHeader('Content-Type', 'application/json'); response.end(JSON.stringify(value));
+ } catch (error) {
+ response.statusCode = 400; response.setHeader('Content-Type', 'application/json');
+ response.end(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
+ }
+ };
+}
diff --git a/labs/nebula/packages/lab/src/server/workflows/compiler/bake.test.ts b/labs/nebula/packages/lab/src/server/workflows/compiler/bake.test.ts
index 15af41f37d..bf078d994e 100644
--- a/labs/nebula/packages/lab/src/server/workflows/compiler/bake.test.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/compiler/bake.test.ts
@@ -7,6 +7,7 @@ import sharp from 'sharp';
import { sha256 } from '@cssearth/volume-bake/compact-inputs/density-grid';
import type { VolumeSliceQuad, VolumeSlices } from '@cssearth/volume-bake/slices/density';
import { bakeCompiler, compilerAlphaDigest, compilerFrame, compilerSliceCounts, verifyCompilerAlphaIdentity } from './bake.ts';
+import { compilerPreparedPoint, compilerPreparedSlices, COMPILER_PHYSICAL_REFERENCE } from '@cssearth/volume-core/coordinates/compiler-frame';
function slices(path: string, bytes: Buffer): VolumeSlices {
const quad: VolumeSliceQuad = { id: 'z-0', axis: 'z', sliceIndex: 0, texturePath: path, widthPx: 2, heightPx: 1,
@@ -26,6 +27,22 @@ test('compiler frame centers absolute west/north/away coordinates without changi
assert.throws(() => compilerFrame({ min: [0, 0, 0], max: [1, 0, 1] }), /finite increasing/);
});
+test('prepared compiler transport reflects source depth exactly once without changing image bytes or UVs', () => {
+ const source = slices('slices/a.png', Buffer.from('pinned pixels'));
+ source.boundsUnits = { min: [-1, -2, -3], max: [4, 5, 6] };
+ const quad = source.quads[0]!;
+ quad.vertices.forEach(point => { point[2] = 2; }); quad.center[2] = 2;
+ const before = structuredClone(source), actual = compilerPreparedSlices(source);
+ assert.deepEqual(source, before);
+ assert.deepEqual(actual.boundsUnits, { min: [-1, -2, -6], max: [4, 5, 3] });
+ assert.deepEqual(actual.quads[0]!.vertices.map(point => point[2]), [-2, -2, -2, -2]);
+ assert.deepEqual(actual.quads[0]!.center, [0, 0, -2]);
+ assert.deepEqual(actual.quads[0]!.normal, [0, 0, 1]);
+ assert.deepEqual(actual.quads[0]!.uvs, quad.uvs); assert.equal(actual.quads[0]!.sha256, quad.sha256);
+ assert.deepEqual(compilerPreparedPoint([1, 2, -3]), [1, 2, 3]);
+ assert.equal(compilerFrame(source.boundsUnits, true).frame.referenceFrame, COMPILER_PHYSICAL_REFERENCE);
+});
+
test('default compiler bake refuses projected-image-only lenses before creating output', async () => {
await assert.rejects(bakeCompiler({ root: tmpdir(), outputDirectory: 'should-not-create-image-extrusion', id: 'invalid-xy-material',
fieldIdentity: '0'.repeat(64), boundsArcsec: { min: [-1, -1, -1], max: [1, 1, 1] },
@@ -36,6 +53,16 @@ test('default compiler bake refuses projected-image-only lenses before creating
}), /3D material sampler/);
});
+test('the actual CSS compiler backend rejects an oversized explicit plan before sampling or writing', async () => {
+ await assert.rejects(bakeCompiler({ root: tmpdir(), outputDirectory: 'should-not-create-oversized-renderer', id: 'oversized',
+ fieldIdentity: '0'.repeat(64), boundsArcsec: { min: [-1, -1, -1], max: [1, 1, 1] },
+ skyBoundsArcsec: { min: [-1, -1], max: [1, 1] },
+ sampling: { sliceCounts: { x: 50, y: 50, z: 52 }, imageWidth: 512, samplesPerSlab: 4 },
+ sampleEmission() { throw new Error('The rejected plan must not sample its field.'); },
+ lenses: [{ id: 'material', label: 'Material', sampleMaterial(_x, _y, _z, out) { out.fill(255); return true; } }],
+ }), /503 retained elements; limit is 500/);
+});
+
test('thin supported features receive finer equally spaced banks without an unbounded slice count', () => {
const bounds = { min: [0, 0, 0] as [number, number, number], max: [960, 480, 120] as [number, number, number] };
const baseline = compilerSliceCounts(bounds), fine = compilerSliceCounts(bounds, 2);
diff --git a/labs/nebula/packages/lab/src/server/workflows/compiler/bake.ts b/labs/nebula/packages/lab/src/server/workflows/compiler/bake.ts
index 220d0533ce..7d2130b416 100644
--- a/labs/nebula/packages/lab/src/server/workflows/compiler/bake.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/compiler/bake.ts
@@ -1,12 +1,13 @@
/** cssEarth renderer adapter for the deterministic internal baker. */
import { bakeCompiler as bake, type BakeCompilerOptions } from '@cssearth/volume-bake/compiler/bake';
-import { compileCssVolume } from '../../../adapters/preparation/css-volume.ts';
+import { compileCssVolume, CSS_COMPILER_RENDER_BUDGET } from '../../../adapters/preparation/css-volume.ts';
import { validatePreparedCssVolume } from '../../../adapters/renderer/volume-validation.ts';
import { prepareCompilerStarSprites } from '../../../adapters/application/star-sprites.ts';
export * from '@cssearth/volume-bake/compiler/bake';
export function bakeCompiler(options: BakeCompilerOptions) {
return bake(options, {
+ renderBudget: CSS_COMPILER_RENDER_BUDGET,
compileVolume: input => validatePreparedCssVolume(compileCssVolume({ ...input, recipe: { anchors: [] } })),
prepareStarSprites: prepareCompilerStarSprites,
});
diff --git a/labs/nebula/packages/lab/src/server/workflows/compiler/compile.ts b/labs/nebula/packages/lab/src/server/workflows/compiler/compile.ts
index 790a3fee96..9a648022bf 100644
--- a/labs/nebula/packages/lab/src/server/workflows/compiler/compile.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/compiler/compile.ts
@@ -14,7 +14,7 @@ import { COMPILER_VERSION, readCompilerRecipe, compilerSourceWeights, type Compi
import { restoreCompilerInputs, type CompilerProgress } from './prerequisites.ts';
import { compilerTarget, loadCompilerImages, compilerImagePanel } from './images.ts';
import { fitEmissionField } from './fit.ts';
-import { createEmissionField } from '@cssearth/volume-core/fields/emission';
+import { createPhotometricEmission } from '@cssearth/volume-core/fields/photometric-emission';
import { createEmissionMaterial } from '@cssearth/volume-core/materials/component-material';
import { loadDepthModel, readDepthRecipe, verifyDepthEvidence } from './depth-model.ts';
import { bakeCompiler } from './bake.ts';
@@ -27,6 +27,7 @@ import type { CompilerPin } from '@cssearth/volume-core/contracts/compiler-bake'
import { compileSampledNebula } from '../sampled-prior/compile.ts';
import { assertCompilerBankIdentity, assertCompilerLensGeometry } from './bank-validation.ts';
import { readEmissionWindow } from '@cssearth/volume-core/fields/emission-window';
+import { loadPhotometricPrior, fitPhotometricEmission, readPhotometricMgeRecipe, verifyPhotometricEvidence, photometricEnvelopeColors } from './photometric-prior.ts';
export async function validateCompilerResult(root: string, value: unknown) {
const result = readCompilerResult(value);
if (result.scene.starSprites) {
@@ -49,6 +50,21 @@ export async function validateCompilerResult(root: string, value: unknown) {
if (geometrySha(evidence) !== recipe.evidence.sha256) throw new TypeError('Saved depth recipe and evidence differ.');
verifyDepthEvidence(recipe, JSON.parse(evidence.toString()));
}
+ if (jointRecord(method) && method.photometricPrior !== undefined) {
+ const prior = method.photometricPrior;
+ if (!jointRecord(prior) || !jointRecord(prior.recipe) || !jointRecord(prior.evidence)) throw new TypeError('Invalid saved photometric model.');
+ const snapshot = async (pin: Record) => {
+ if (!jointPath(pin.path) || !pin.path.startsWith(`.local/nebula-lab/compiler/${result.id}/`) ||
+ typeof pin.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(pin.sha256)) throw new TypeError('Invalid photometric evidence snapshot.');
+ return readGeometryPin(root, { path: pin.path, sha256: pin.sha256 });
+ };
+ const recipe = readPhotometricMgeRecipe(JSON.parse((await snapshot(prior.recipe)).toString()));
+ const evidenceBytes = await snapshot(prior.evidence);
+ if (geometrySha(evidenceBytes) !== recipe.evidence.sha256) throw new TypeError('Saved photometric evidence differs.');
+ const compilerRecipe = readCompilerRecipe(method.recipe);
+ if (!compilerRecipe.photometricPriorRecipe) throw new TypeError('Saved compiler recipe omits its photometric prior.');
+ verifyPhotometricEvidence(recipe, JSON.parse(evidenceBytes.toString()), compilerRecipe.id);
+ }
async function readBank(pin: CompilerPin) {
const volume = validatePreparedCssVolume(JSON.parse((await readGeometryPin(root, pin)).toString())), directory = pin.path.slice(0, pin.path.lastIndexOf('/') + 1);
for (const resource of volume.resources) await readGeometryPin(root, { path: directory + resource.path, sha256: resource.sha256 });
@@ -77,6 +93,12 @@ export async function compileNebula(root: string, request: CompilerRequest, sign
const evidenceStarted = performance.now();
if (recipe.depthRecipe) progress('Verifying physical evidence and depth assumptions…', .19);
const depthModel = recipe.depthRecipe ? await loadDepthModel(root, recipe.depthRecipe, recipe.id) : undefined;
+ const photometricModel = recipe.photometricPriorRecipe ? await loadPhotometricPrior(root, recipe.photometricPriorRecipe, recipe.id) : undefined;
+ if (photometricModel) {
+ if (photometricModel.recipe.centerIcrsDegrees.some((value, index) => Math.abs(value - observations.frame.centerIcrsDegrees[index]!) > 1e-8))
+ throw new TypeError('Photometric model and registered images use different sky origins.');
+ pipeline.push({ id: 'photometric-model', label: 'Verify published light distribution', state: 'complete', seconds: (performance.now() - evidenceStarted) / 1000 });
+ }
if (depthModel) {
if (depthModel.recipe.centerIcrsDegrees.some((value, index) => Math.abs(value - observations.frame.centerIcrsDegrees[index]!) > 1e-8))
throw new TypeError('Depth model and registered images use different sky origins.');
@@ -106,6 +128,7 @@ export async function compileNebula(root: string, request: CompilerRequest, sign
sourceLayers: sourceData.images.map(image => [image.id, image.original.sha256, image.diffuse.sha256, image.stars.sha256]),
molecular: joint && { recipe: joint.recipeSha256, evidence: joint.evidence },
physicalDepth: depthModel && { recipe: depthModel.recipeSha256, evidence: depthModel.recipe.evidence.sha256 },
+ photometricPrior: photometricModel && { recipe: photometricModel.recipeSha256, evidence: photometricModel.recipe.evidence.sha256 },
request: { controls: request.controls, evidence: { sensitivity: request.evidence.sensitivity, weights } } }));
const directory = `.local/nebula-lab/compiler/${id}`, receipt = resolve(root, directory, 'result.json');
try { const cached = await validateCompilerResult(root, JSON.parse(await readFile(receipt, 'utf8'))); progress('Prepared nebula restored', 1); return cached; }
@@ -117,14 +140,15 @@ export async function compileNebula(root: string, request: CompilerRequest, sign
let started = performance.now();
if (joint) progress('Fitting the velocity scaffold…', .22);
const scaffoldFit = joint ? fitJointModels(joint.evidence, defaultJointControls, joint.recipe, message => progress(message, .24), signal) : undefined;
- if (!depthModel) pipeline.push({ id: 'scaffold', label: joint ? 'Fit velocity scaffold' : 'Explicit image-only depth prior', state: 'complete', seconds: (performance.now() - started) / 1000 });
+ if (!depthModel && !photometricModel) pipeline.push({ id: 'scaffold', label: joint ? 'Fit velocity scaffold' : 'Explicit image-only depth prior', state: 'complete', seconds: (performance.now() - started) / 1000 });
started = performance.now(); progress('Fitting the complete emission structure…', .3);
- const fitted = fitEmissionField({ ...target, scaffold: scaffoldFit?.fits[0]?.parameters,
+ const photometricFit = photometricModel ? fitPhotometricEmission(target, request.controls, photometricModel, signal, message => progress(message, .36)) : undefined;
+ const fitted = photometricFit ?? fitEmissionField({ ...target, scaffold: scaffoldFit?.fits[0]?.parameters,
velocityCoverage: joint?.evidence.velocities.map(p => ({ x: p.x, y: p.y, radiusArcsec: joint!.evidence.beamFwhmArcsec / 2 })) }, request.controls,
{ signal, depthRecipe: depthModel?.recipe, onProgress: message => progress(message, .36) });
if (fitted.field.components.length === 0) throw new Error('No usable nebular emission survived. Inspect source alignment and star removal.');
- const field = createEmissionField(fitted.field), model = await save('field.json', Buffer.from(JSON.stringify(fitted.field)));
- pipeline.push({ id: depthModel ? 'depth-model' : 'field', label: depthModel ? 'Fit emission on evidence-guided surfaces' : 'Fit 3D emission components', state: 'complete', seconds: (performance.now() - started) / 1000 });
+ const field = createPhotometricEmission(fitted.field), model = await save('field.json', Buffer.from(JSON.stringify(fitted.field)));
+ pipeline.push({ id: depthModel ? 'depth-model' : 'field', label: photometricModel ? 'Fit finite light inside the published model' : depthModel ? 'Fit emission on evidence-guided surfaces' : 'Fit 3D emission components', state: 'complete', seconds: (performance.now() - started) / 1000 });
progress('Placing observed compact lights in the inferred field…', .42); started = performance.now();
const union = recipe.starCatalogue ? await compilerUnionStars(source, fitted.field, recipe.maximumStars, sourceData.images, recipe.starCatalogue) : undefined;
const catalogue = observedStarsBytes ? prepareCatalogueStars(JSON.parse(observedStarsBytes.toString()), fitted.field, center, recipe.maximumStars, sourceData.images.map(image => image.id)) : undefined;
@@ -135,10 +159,14 @@ export async function compileNebula(root: string, request: CompilerRequest, sign
const span = Math.max(field.bounds.max[0] - field.bounds.min[0], field.bounds.max[1] - field.bounds.min[1]) * 1.04;
const skyBounds = { min: [centerX - span / 2, centerY - span / 2] as [number, number], max: [centerX + span / 2, centerY + span / 2] as [number, number] };
started = performance.now();
- const materials = sourceData.images.map(image => ({ image, ...createEmissionMaterial(fitted.field, image, field) }));
+ const materials = sourceData.images.map(image => {
+ const material = createEmissionMaterial(fitted.field, image, field.finite), envelopeColors = photometricEnvelopeColors(fitted.field, image);
+ return { image, receipt: { ...material.receipt, ...(envelopeColors ? { envelopeColors } : {}) },
+ sampleMaterial: field.createMaterialSampler(material.receipt.components, envelopeColors) };
+ });
const scene = await bakeCompiler({ root, outputDirectory: `${directory}/scene`, id, fieldIdentity: fitted.field.identity,
boundsArcsec: field.bounds, skyBoundsArcsec: skyBounds, sampleEmission: field.sampleEmission, stars,
- minimumFeatureScaleArcsec: depthModel ? Math.min(...fitted.field.components.flatMap(component => component.sigma)) : undefined,
+ minimumFeatureScaleArcsec: depthModel || photometricModel ? Math.min(...fitted.field.components.flatMap(component => component.sigma)) : undefined,
lenses: materials.map(material => ({ id: material.image.id, label: material.image.label, sampleMaterial: material.sampleMaterial })), signal,
progress: value => progress(value.message, value.phase === 'volume' ? .45 + .2 * value.completed / value.total : value.phase === 'texture' ? .65 + .25 * value.completed / value.total : .92) });
pipeline.push({ id: 'bake', label: 'Bake shared geometry + image lenses', state: 'complete', seconds: (performance.now() - started) / 1000 });
@@ -159,8 +187,14 @@ export async function compileNebula(root: string, request: CompilerRequest, sign
recipe: await save('depth-recipe.json', depthModel.recipeBytes), evidence: await save('physical-evidence.json', depthModel.evidenceBytes),
methods: depthModel.methods, interpretation: depthModel.recipe.interpretation, assignments: fitted.field.depthConstraints,
} : undefined;
+ const photometricPrior = photometricModel && photometricFit ? {
+ recipe: await save('photometric-model.json', photometricModel.recipeBytes), evidence: await save('photometric-evidence.json', photometricModel.evidenceBytes),
+ interpretation: photometricModel.recipe.interpretation,
+ assignments: photometricFit.depthAssignments, settings: photometricFit.settings,
+ ...('envelopeMetrics' in photometricFit ? { envelopeMetrics: photometricFit.envelopeMetrics } : {}),
+ } : undefined;
const method = await save('method.json', Buffer.from(JSON.stringify({ version: COMPILER_VERSION, implementation, recipe, recipeSha256: geometrySha(recipeBytes), request,
- physicalDepth, ...(catalogue ? { observedStars: { source: recipe.observedStars, ...catalogue.receipt } } : {}), ...(union ? { starCatalogue: union.selection } : {}),
+ physicalDepth, photometricPrior, ...(catalogue ? { observedStars: { source: recipe.observedStars, ...catalogue.receipt } } : {}), ...(union ? { starCatalogue: union.selection } : {}),
inputIdentity: inputs.identity, target: { ...target, target: undefined, coverage: undefined }, scaffoldFit, fieldMetrics: fitted.metrics,
assumptions: fitted.field.assumptions, stars: catalogue ? 'Measured optical catalogue overlay, apparent V ranked, independent of image lens. See observedStars receipt for color and authored depth limits.' : union ? union.selection.interpretation : 'Compact points detected once from the reference stellar residual. Each lens preserves its own local background-subtracted residual aperture display energy and angular footprint at the same registered xy; absent coverage or residual emits zero light. Only columns with fitted emission are included. Depth is a deterministic conditional field sample, unchanged across lenses, not a measured stellar distance or confirmed membership. Encoded RGB display accounting is not calibrated stellar flux, and stars visible only outside the reference catalogue are not added.',
materials: materials.map(material => material.receipt),
diff --git a/labs/nebula/packages/lab/src/server/workflows/compiler/compiler-defaults.test.ts b/labs/nebula/packages/lab/src/server/workflows/compiler/compiler-defaults.test.ts
index a0fbeeebe6..f260089240 100644
--- a/labs/nebula/packages/lab/src/server/workflows/compiler/compiler-defaults.test.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/compiler/compiler-defaults.test.ts
@@ -15,6 +15,13 @@ test('recipe defaults reach unsaved controls while explicit user choices win', (
assert.deepEqual(compilerControlsForRecipe(readCompilerRecipe(source)), defaultCompilerControls);
});
+test('photometric recipes cannot configure the hidden depth control away from their pinned prior', () => {
+ const photometricPriorRecipe = 'labs/nebula/models/example/photometric-mge.json';
+ assert.equal(compilerControlsForRecipe(readCompilerRecipe({ ...source, photometricPriorRecipe })).depth, 1);
+ assert.throws(() => readCompilerRecipe({ ...source, photometricPriorRecipe,
+ defaultControls: { detail: .9, faint: .15, depth: 1.2 } }), /require depth=1/);
+});
+
test('named source weights follow source IDs and explicit diagnostic weights override', () => {
const recipe = readCompilerRecipe({ ...source, sourceWeights: { optical: 1, infrared: .15 } });
assert.deepEqual(compilerSourceWeights(recipe, ['infrared', 'optical', 'third']), [.15, 1, 1]);
diff --git a/labs/nebula/packages/lab/src/server/workflows/compiler/photometric-prior.test.ts b/labs/nebula/packages/lab/src/server/workflows/compiler/photometric-prior.test.ts
new file mode 100644
index 0000000000..cbbe5f342d
--- /dev/null
+++ b/labs/nebula/packages/lab/src/server/workflows/compiler/photometric-prior.test.ts
@@ -0,0 +1,94 @@
+import assert from 'node:assert/strict';
+import { test } from 'node:test';
+import { readFile } from 'node:fs/promises';
+import { readPhotometricMgeRecipe, verifyPhotometricEvidence } from './photometric-prior.ts';
+
+test('photometric evidence validation rejects cross-subject or repinned source snapshots', async () => {
+ const recipe = readPhotometricMgeRecipe(JSON.parse(await readFile('labs/nebula/models/omega-centauri/photometric-mge.json', 'utf8')));
+ const evidence: unknown = JSON.parse(await readFile(recipe.evidence.path, 'utf8'));
+ verifyPhotometricEvidence(recipe, evidence, recipe.id);
+ assert.throws(() => verifyPhotometricEvidence(recipe, evidence, 'other'), /another subject/);
+ assert.throws(() => verifyPhotometricEvidence(recipe, { schema: 'cssearth-nebula-physical-evidence@1', subjectId: 'other', sources: [] }, recipe.id), /evidence ledger/);
+ assert.throws(() => verifyPhotometricEvidence({ ...recipe, source: { ...recipe.source, sha256: '0'.repeat(64) } }, evidence, recipe.id), /evidence ledger/);
+});
+
+import { fitPhotometricEmission } from './photometric-prior.ts';
+import { createPhotometricMgePrior } from '@cssearth/nebula-reconstruction/methods/inference/photometric-mge';
+import { createPhotometricEmission } from '@cssearth/volume-core/fields/photometric-emission';
+import { readRetainedEmissionField } from '@cssearth/volume-core/fields/retained-emission';
+import { geometrySha } from '../geometry/registered-source.ts';
+
+test('two-scale fit retains smooth light and finite residuals, including after JSON replay', async () => {
+ const published = readPhotometricMgeRecipe(JSON.parse(await readFile('labs/nebula/models/omega-centauri/photometric-mge.json', 'utf8')));
+ const recipe = readPhotometricMgeRecipe({ ...published, residualMaximumComponents: undefined, gaussians: [{ centralAmplitude: 1, sigmaArcsec: 100, projectedAxisRatio: 1 }],
+ envelope: { scalePixels: 3, fraction: .95, floor: 0, depthSamples: 128, depthTrim: .001 } });
+ const width = 32, bounds = { min: [-300, -300] as [number, number], max: [300, 300] as [number, number] };
+ const target = Float32Array.from({ length: width * width }, (_, p) => {
+ const x = -300 + (p % width + .5) * 600 / width, y = 300 - (Math.floor(p / width) + .5) * 600 / width;
+ return Math.exp(-.5 * (x * x + y * y) / 10000) + .5 * Math.exp(-.5 * ((x - 40) ** 2 + (y - 30) ** 2) / 150);
+ });
+ const fit = fitPhotometricEmission({ target, coverage: new Uint8Array(target.length).fill(1), width, height: width, bounds },
+ { detail: 1, faint: .35, depth: 1 }, { recipe, prior: createPhotometricMgePrior(recipe), recipeBytes: Buffer.from(''), evidenceBytes: Buffer.from(''), recipeSha256: geometrySha('fixture') },
+ new AbortController().signal, () => {});
+ assert.ok(fit.field.photometricEnvelope);
+ assert.ok(fit.field.components.length > 0);
+ assert.ok('envelopeMetrics' in fit && fit.envelopeMetrics.envelopeLightFraction > .8);
+ const original = createPhotometricEmission(fit.field), replay = createPhotometricEmission(readRetainedEmissionField(JSON.parse(JSON.stringify(fit.field))));
+ const missing = createPhotometricEmission({ ...fit.field, photometricEnvelope: undefined });
+ let fullColumn = 0, missingColumn = 0;
+ const a = [0, 0, 0], b = [0, 0, 0], c = [0, 0, 0];
+ for (let z = -600; z < 600; z += 2) {
+ original.sampleEmission(0, 0, z, a); replay.sampleEmission(0, 0, z, b); missing.sampleEmission(0, 0, z, c);
+ assert.deepEqual(a, b); fullColumn += a[0]! * 2; missingColumn += c[0]! * 2;
+ }
+ assert.ok(fullColumn > .7 && fullColumn < 1.2);
+ assert.ok(missingColumn < fullColumn * .3, 'dropping the envelope must lose the broad light');
+ const colors = fit.field.components.map(component => ({ id: component.id, rgb: [255, 100, 80] as [number, number, number], covered: true }));
+ assert.throws(() => replay.createMaterialSampler(colors), /envelope colors/);
+ const envelopeColors = { width: 2, height: 2, rgb: Array.from({ length: 12 }, (_, i) => [100, 160, 255][i % 3]!) };
+ const paint = original.createMaterialSampler(colors, envelopeColors), copied = replay.createMaterialSampler(colors, envelopeColors);
+ for (const z of [-150, 0, 150]) {
+ const x: [number, number, number] = [0, 0, 0], y: [number, number, number] = [0, 0, 0];
+ assert.equal(paint(0, 0, z, x), copied(0, 0, z, y)); assert.deepEqual(x, y);
+ }
+});
+
+import { fitEmissionField } from '@cssearth/nebula-reconstruction/methods/inference/fit';
+test('authored residual budget bounds the fit and omitted budgets preserve historical defaults', async () => {
+ const width = 48, target = Float32Array.from({ length: width * width }, (_, p) => (Math.sin(p * 41.71) + 1) / 2);
+ const input = { width, height: width, target, bounds: { min: [-100, -100] as [number, number], max: [100, 100] as [number, number] } };
+ const controls = { detail: 1, faint: 1, depth: 1 };
+ const bounded = fitEmissionField(input, controls, { externalDepthAssignment: true, maximumComponents: 16 });
+ assert.ok(bounded.field.components.length <= 16 && bounded.field.components.length > 0);
+ assert.equal(bounded.metrics.iterations, 16);
+ const recipe = JSON.parse(await readFile('labs/nebula/models/omega-centauri/photometric-mge.json', 'utf8'));
+ assert.equal(readPhotometricMgeRecipe({ ...recipe, residualMaximumComponents: 4096 }).residualMaximumComponents, 4096);
+ delete recipe.residualMaximumComponents;
+ assert.equal(readPhotometricMgeRecipe(recipe).residualMaximumComponents, undefined);
+ assert.throws(() => fitEmissionField(input, controls, { maximumComponents: 8193 }), /budget/);
+ assert.throws(() => readPhotometricMgeRecipe({ ...recipe, residualMaximumComponents: 1.5 }), /budget/);
+});
+
+import type { EmissionFieldModel } from '@cssearth/volume-core/contracts/emission';
+test('validated envelope chroma bounds the exact round2 green-channel roundoff without accepting nonfinite colors', async () => {
+ // Real x-49 texel: [-1362.6885062830177,-664.6052184385982,1306.7023521967885] arcsec.
+ // Its finite light (1.276095724233293e-23) is below one ULP of the envelope light;
+ // this single Gaussian reproduces that exact envelope weight without a multi-megabyte fixture.
+ const light = 0.0000020943697978687045, sigmaArcsec = 1 / (Math.sqrt(2 * Math.PI) * light);
+ assert.equal(light * 255 / light, 255.00000000000003);
+ const published = JSON.parse(await readFile('labs/nebula/models/omega-centauri/photometric-mge.json', 'utf8'));
+ const recipe = readPhotometricMgeRecipe({ ...published, inclinationDegrees: 90,
+ gaussians: [{ centralAmplitude: 1, sigmaArcsec, projectedAxisRatio: 1 }] });
+ const skyBounds = { min: [-1, -1] as [number, number], max: [1, 1] as [number, number] };
+ const model: EmissionFieldModel = { schema: 'cssearth-conditional-emission-field@1', identity: 'e'.repeat(64),
+ controls: { detail: 1, faint: .35, depth: 1 }, components: [], bounds: { min: [-1, -1, -1], max: [1, 1, 1] }, skyBounds, scaffold: null,
+ assumptions: { kernel: 'regression fixture', projectionUnits: 'relative light', depth: 'regression fixture', halo: 'none', haloRadiusArcsec: 1, equalNearFarSplit: false, velocityUncoveredComponents: 0 },
+ photometricEnvelope: { schema: 'cssearth-photometric-envelope@1', priorIdentity: 'a'.repeat(64), recipe,
+ width: 2, height: 2, bounds: skyBounds, zRange: [-1, 1], gain: [1, 1, 1, 1] } };
+ const field = createPhotometricEmission(model), out: [number, number, number] = [0, 0, 0];
+ field.sampleEmission(0, 0, 0, out); assert.equal(out[0], light);
+ const colors = { width: 2, height: 2, rgb: Array.from({ length: 12 }, (_, i) => [198.46347151697145, 255, 224.6307247429958][i % 3]!) };
+ assert.equal(field.createMaterialSampler([], colors)(0, 0, 0, out), true);
+ assert.equal(out[1], 255); assert.ok(out.every(c => Number.isFinite(c) && c >= 0 && c <= 255));
+ assert.throws(() => field.createMaterialSampler([], { ...colors, rgb: colors.rgb.map(() => Infinity) }), /envelope colors/);
+});
diff --git a/labs/nebula/packages/lab/src/server/workflows/compiler/photometric-prior.ts b/labs/nebula/packages/lab/src/server/workflows/compiler/photometric-prior.ts
new file mode 100644
index 0000000000..2f66ad24c8
--- /dev/null
+++ b/labs/nebula/packages/lab/src/server/workflows/compiler/photometric-prior.ts
@@ -0,0 +1,97 @@
+import { readFile, realpath } from 'node:fs/promises';
+import { resolve, relative, isAbsolute } from 'node:path';
+import { geometrySha } from '../geometry/registered-source.ts';
+import { jointPath, jointRecord } from '../../../features/joint-fit/model.ts';
+import { readPhotometricMgeRecipe as readScientificRecipe, createPhotometricMgePrior, type PhotometricMgeRecipe } from '@cssearth/nebula-reconstruction/methods/inference/photometric-mge';
+import { fitSimulationGuidedEmission, type SimulationDepthSettings } from '@cssearth/nebula-reconstruction/methods/inference/simulation-guided';
+import { fitSimulationEnvelope } from '@cssearth/nebula-reconstruction/methods/inference/simulation-envelope';
+import { createPhotometricEmission, type EnvelopeColors } from '@cssearth/volume-core/fields/photometric-emission';
+import { envelopeChromaticity, envelopeChromaSettings, pixelCenter } from '@cssearth/volume-core/fields/simulation-envelope';
+import type { MaterialImage } from '@cssearth/volume-core/materials/component-material';
+import type { EmissionFieldModel } from '@cssearth/volume-core/contracts/emission';
+import type { EmissionFitInput } from '@cssearth/volume-core/contracts/emission';
+export function readPhotometricMgeRecipe(value: unknown): PhotometricMgeRecipe {
+ const recipe = readScientificRecipe(value);
+ if (!recipe.evidence.path.startsWith('labs/nebula/models/')) throw new TypeError('Photometric evidence must be object-owned.');
+ return recipe;
+}
+
+/** Live compilation and saved-result restoration enforce the same scientific source identity. */
+export function verifyPhotometricEvidence(recipe: PhotometricMgeRecipe, evidence: unknown, subjectId: string): void {
+ if (recipe.id !== subjectId) throw new TypeError('Photometric model belongs to another subject.');
+ if (!jointRecord(evidence) || evidence.schema !== 'cssearth-nebula-physical-evidence@1' || evidence.subjectId !== subjectId ||
+ !Array.isArray(evidence.sources) || !evidence.sources.some(row => jointRecord(row) && jointRecord(row.download) &&
+ row.download.url === recipe.source.url && row.download.sha256 === recipe.source.sha256))
+ throw new TypeError('Photometric model lacks its source-owned evidence ledger.');
+}
+
+export async function loadPhotometricPrior(root: string, path: string, subjectId: string) {
+ if (!jointPath(path) || !path.startsWith('labs/nebula/models/')) throw new TypeError('Photometric model must be object-owned.');
+ const source = async (name: string) => {
+ const actual = await realpath(resolve(root, name)), offset = relative(await realpath(root), actual);
+ if (offset === '..' || offset.startsWith('../') || isAbsolute(offset)) throw new TypeError('Photometric source leaves the repository.');
+ return readFile(actual);
+ };
+ const recipeBytes = await source(path), recipe = readPhotometricMgeRecipe(JSON.parse(recipeBytes.toString()));
+ const evidenceBytes = await source(recipe.evidence.path);
+ if (geometrySha(evidenceBytes) !== recipe.evidence.sha256) throw new TypeError('Photometric evidence hash changed.');
+ const evidence: unknown = JSON.parse(evidenceBytes.toString());
+ verifyPhotometricEvidence(recipe, evidence, subjectId);
+ return { recipe, recipeBytes, evidenceBytes, recipeSha256: geometrySha(recipeBytes), prior: createPhotometricMgePrior(recipe) };
+}
+
+/** Finite light features receive one conditional depth realization, not an extruded image or claimed stellar distance. */
+export function fitPhotometricEmission(input: EmissionFitInput, controls: unknown,
+ model: Awaited>, signal: AbortSignal, onProgress: (message: string) => void) {
+ const pixelPitch = Math.max((input.bounds.max[0] - input.bounds.min[0]) / input.width,
+ (input.bounds.max[1] - input.bounds.min[1]) / input.height);
+ const settings: SimulationDepthSettings = { depthSamples: 2048, modeRelativeThreshold: .1, maximumModes: 1,
+ minimumSigmaZ: pixelPitch, maximumSigmaZ: Math.max(pixelPitch, ...model.recipe.gaussians.map(row => row.sigmaArcsec)),
+ featureThicknessRatio: 1, supportSigma: 4, placement: 'conditional-quantile' };
+ const coverage = input.coverage ?? new Uint8Array(input.target.length).fill(1);
+ const envelope = model.recipe.envelope && fitSimulationEnvelope({ ...input, coverage }, model.prior, model.recipe.envelope, signal);
+ const detail = envelope ? input.target.map((value, p) => Math.max(0, value - envelope.projection[p]!)) : input.target;
+ const fitted = fitSimulationGuidedEmission({ ...input, target: detail }, controls, model.prior, settings, { signal, onProgress, maximumComponents: model.recipe.residualMaximumComponents });
+ if (!envelope) return fitted;
+ fitted.field.photometricEnvelope = { schema: 'cssearth-photometric-envelope@1', priorIdentity: model.prior.identity,
+ recipe: model.recipe, ...envelope.grid, gain: Array.from(envelope.grid.gain) };
+ fitted.field.identity = geometrySha(JSON.stringify({ finiteIdentity: fitted.field.identity, envelope: fitted.field.photometricEnvelope }));
+ fitted.field.bounds = createPhotometricEmission(fitted.field).bounds;
+ fitted.field.assumptions.depth += ' The smooth envelope retains the oblate MGE density at every depth; only positive residual features receive conditional quantile depths.';
+ let targetSum = 0, modeledSum = 0, missing = 0, excess = 0, before = 0, after = 0, count = 0;
+ for (let p = 0; p < input.target.length; p++) {
+ fitted.projection[p] += envelope.projection[p]!;
+ if (!coverage[p]) continue;
+ const target = input.target[p]!, residual = target - fitted.projection[p]!;
+ fitted.residual[p] = residual; fitted.unassigned[p] = Math.max(0, residual);
+ targetSum += target; modeledSum += fitted.projection[p]!; missing += Math.max(0, residual); excess += Math.max(0, -residual);
+ before += target * target; after += residual * residual; count++;
+ }
+ Object.assign(fitted.metrics, { targetSum, modeledSum, unassignedSum: missing, excessSum: excess,
+ beforeRmse: Math.sqrt(before / Math.max(1, count)), afterRmse: Math.sqrt(after / Math.max(1, count)), relativeSquaredError: after / Math.max(1e-12, before) });
+ return { ...fitted, envelopeMetrics: envelope.metrics };
+}
+
+/** Retain only envelope-scale chromaticity; fine image detail stays attached to finite XYZ emitters. */
+export function photometricEnvelopeColors(model: EmissionFieldModel, image: MaterialImage): EnvelopeColors | undefined {
+ const envelope = model.photometricEnvelope;
+ if (!envelope) return undefined;
+ const settings = envelope.recipe.envelope!;
+ const rgb = new Float32Array(envelope.width * envelope.height * 3), coverage = new Uint8Array(envelope.width * envelope.height), pixel: [number, number, number] = [0, 0, 0];
+ for (let p = 0; p < coverage.length; p++) {
+ const [x, y] = pixelCenter(envelope.bounds, envelope.width, envelope.height, p);
+ if (image.sampleRgb(x, y, pixel)) { coverage[p] = 1; for (let c = 0; c < 3; c++) rgb[p * 3 + c] = pixel[c]!; }
+ }
+ const chroma = envelopeChromaSettings(settings);
+ const sample = envelopeChromaticity(rgb, coverage, envelope.width, envelope.height, envelope.bounds, settings.scalePixels,
+ chroma.halfSaturationQuantile, chroma.skyQuantile, chroma.coverageTaper);
+ // Two samples per smoothing sigma retain only the coarse envelope chromaticity.
+ const width = Math.max(2, Math.min(envelope.width, Math.ceil(2 * envelope.width / settings.scalePixels)));
+ const height = Math.max(2, Math.min(envelope.height, Math.ceil(2 * envelope.height / settings.scalePixels)));
+ const colors = new Float32Array(width * height * 3);
+ for (let p = 0; p < width * height; p++) {
+ const [x, y] = pixelCenter(envelope.bounds, width, height, p);
+ sample(x, y, pixel); for (let c = 0; c < 3; c++) colors[p * 3 + c] = pixel[c]!;
+ }
+ return { width, height, rgb: Array.from(colors) };
+}
diff --git a/labs/nebula/packages/lab/src/server/workflows/compiler/refresh-stars.ts b/labs/nebula/packages/lab/src/server/workflows/compiler/refresh-stars.ts
index f80d81ed3b..b02c0ba810 100644
--- a/labs/nebula/packages/lab/src/server/workflows/compiler/refresh-stars.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/compiler/refresh-stars.ts
@@ -11,6 +11,7 @@ import { COMPILER_STAR_PROFILE_PATH, prepareCompilerStarSprites } from '../../..
import { validateCompilerResult } from './compile.ts';
import { readCompilerResult } from '../../../features/compiler/result.ts';
import { readRetainedEmissionField } from '@cssearth/volume-core/fields/retained-emission';
+import { COMPILER_PHYSICAL_REFERENCE } from '@cssearth/volume-core/coordinates/compiler-frame';
export async function refreshCompilerStars(root: string, recipePath: string, previousResultPath: string) {
const started = performance.now();
@@ -34,8 +35,9 @@ export async function refreshCompilerStars(root: string, recipePath: string, pre
if (sourceBytes && geometrySha(sourceBytes) !== recipe.observedStars!.sha256) throw new TypeError('Observed stellar source changed.');
const model = readRetainedEmissionField(JSON.parse((await readGeometryPin(root, base.model)).toString()));
const origin = base.scene.coordinates.localOriginArcsec;
+ const depthSign = base.scene.frame.referenceFrame === COMPILER_PHYSICAL_REFERENCE ? -1 : 1;
const prepared = sourceBytes ? prepareCatalogueStars(JSON.parse(sourceBytes.toString()), model, observations.frame.centerIcrsDegrees, recipe.maximumStars, base.sources.map(source => source.id)) :
- { stars: base.scene.stars.map(({ positionUnits, ...star }) => ({ ...star, positionArcsec: [positionUnits[0] + origin[0], positionUnits[1] + origin[1], positionUnits[2] + origin[2]] as [number, number, number] })),
+ { stars: base.scene.stars.map(({ positionUnits, ...star }) => ({ ...star, positionArcsec: [positionUnits[0] + origin[0], positionUnits[1] + origin[1], positionUnits[2] * depthSign + origin[2]] as [number, number, number] })),
receipt: { method: 'retained-stellar-photometry@1', selectedCount: base.scene.stars.length, interpretation: 'Existing residual-derived positions and per-lens light retained exactly; only the prepared point profile changes. Not a newly measured catalogue.' } };
const implementation = await implementationPins(root, ['labs/nebula/packages/lab/src/server/workflows/compiler/refresh-stars.ts']);
const id = geometrySha(JSON.stringify({ method: 'retained-cloud-stellar-refresh@1', base: geometrySha(bytes), recipe: geometrySha(recipeBytes), implementation,
@@ -57,7 +59,7 @@ export async function refreshCompilerStars(root: string, recipePath: string, pre
retainedCloud: { result: { path: previousResultPath, sha256: geometrySha(bytes) }, method: base.method, volumeId: base.scene.volumeId ?? base.id } }, null, 2)));
const starSprites = await prepareCompilerStarSprites(root, directory, prepared.stars);
const stars = prepared.stars.map(({ positionArcsec, ...star }) => ({ ...star,
- positionUnits: [positionArcsec[0] - origin[0], positionArcsec[1] - origin[1], positionArcsec[2] - origin[2]] }));
+ positionUnits: [positionArcsec[0] - origin[0], positionArcsec[1] - origin[1], (positionArcsec[2] - origin[2]) * depthSign] }));
const result = readCompilerResult({ ...base, id, method, scene: { ...base.scene, id, volumeId: base.scene.volumeId ?? base.id, stars, ...starSprites },
metrics: { ...base.metrics, stars: stars.length }, pipeline: [...base.pipeline.filter(stage => stage.id !== 'stars'),
{ id: 'stars', label: recipe.observedStars ? 'Prepare measured catalogue lights · retained cloud' : 'Prepare soft stellar profiles · retained cloud', state: 'complete', seconds: (performance.now() - started) / 1000 }] });
diff --git a/labs/nebula/packages/lab/src/server/workflows/compiler/retained-material-bank.test.ts b/labs/nebula/packages/lab/src/server/workflows/compiler/retained-material-bank.test.ts
index 9c64ef3203..e732b9d412 100644
--- a/labs/nebula/packages/lab/src/server/workflows/compiler/retained-material-bank.test.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/compiler/retained-material-bank.test.ts
@@ -13,11 +13,17 @@ import { readCompilerBakeResult } from '@cssearth/volume-core/contracts/compiler
import { assertCompilerLensGeometry } from './bank-validation.ts';
import { prepareRetainedMaterialBank, type RetainedMaterialBankOptions } from './retained-material-bank.ts';
-async function fixture(t: TestContext) {
+async function fixture(t: TestContext, variable = false) {
const root = await mkdtemp(join(tmpdir(), 'retained-material-')); t.after(() => rm(root, { recursive: true, force: true }));
const boundsArcsec = { min: [10, 20, 30] as [number, number, number], max: [12, 22, 32] as [number, number, number] };
const { frame, localBounds, origin } = compilerFrame(boundsArcsec), fieldIdentity = 'a'.repeat(64);
+ const layerPlan = variable ? { schema: 'cssearth-volume-layer-plan@1' as const,
+ referenceSliceCounts: { x: 4, y: 4, z: 4 }, referenceSamplesPerSlab: 4,
+ axes: { x: [{ startCell: 0, endCell: 1 }, { startCell: 1, endCell: 4 }],
+ y: [{ startCell: 0, endCell: 2 }, { startCell: 2, endCell: 4 }],
+ z: [{ startCell: 0, endCell: 3 }, { startCell: 3, endCell: 4 }] } } : undefined;
const baked = await bakeMasterVolumeSlices({ boundsKpc: localBounds, sliceCounts: { x: 2, y: 2, z: 2 }, samplesPerSlab: 4,
+ ...(layerPlan ? { layerPlan } : {}),
exposureGain: 1, masterWidth: 8, masterDirectory: join(root, 'masters'), deliveryBanks: [
{ width: 8, outputDirectory: join(root, 'neutral'), imageEncoding: { format: 'png' } }], unitsPerSourceUnit: 1,
provenance: {}, cropTransparent: true, sampleEmission(x, y, z, out) { out[0] = out[1] = out[2] = Math.hypot(x, y, z) < .9 ? .2 : 0; },
@@ -33,7 +39,7 @@ async function fixture(t: TestContext) {
coordinates: { axes: ['west', 'north', 'away'], localOriginArcsec: origin, earthView: 'observer-at-negative-z-looking-away' },
neutral, alphaSha256, lenses: [{ id: 'original', label: 'Original', volume: neutral,
coverage: { positiveAlphaTexels: 0, recoloredTexels: 0, outsideImageTexels: 0 } }], stars: [],
- sampling: { sliceCounts: { x: 2, y: 2, z: 2 }, imageWidth: 512, samplesPerSlab: 4 } });
+ sampling: { sliceCounts: { x: 2, y: 2, z: 2 }, imageWidth: 512, samplesPerSlab: 4, ...(layerPlan ? { layerPlan } : {}) } });
const options: RetainedMaterialBankOptions = { root, outputDirectory: 'painted', scene,
neutralSlicesPin: await pin('neutral/volume-slices.json', slices),
sampleEmission(x, y, z, out) { out[0] = out[1] = out[2] = Math.hypot(x - origin[0], y - origin[1], z - origin[2]) < .9 ? .2 : 0; },
@@ -61,6 +67,24 @@ test('retained material prepares RGB through the existing slab/compiler path wit
assert.notEqual(painted.resources[0]!.sha256, neutral.resources[0]!.sha256);
});
+test('retained nonuniform material preserves intervals and rejects missing replay metadata', async t => {
+ const { options, slices, pin } = await fixture(t, true);
+ const lens = await prepareRetainedMaterialBank(options);
+ assert.ok(lens.coverage.recoloredTexels > 0);
+ const painted = JSON.parse(await readFile(join(options.root, 'painted/volume-slices.json'), 'utf8'));
+ assert.deepEqual(painted.approximation.layerPlan, options.scene.sampling.layerPlan);
+ assert.deepEqual(painted.quads.map((q: { slab: unknown }) => q.slab), slices.quads.map(q => q.slab));
+ for (const mutate of [
+ () => { delete slices.quads[0]!.slab; },
+ () => { delete slices.approximation.layerPlan; },
+ () => { slices.quads[0]!.slab!.samples--; },
+ ]) {
+ const before = structuredClone(slices); mutate();
+ await assert.rejects(prepareRetainedMaterialBank({ ...options, neutralSlicesPin: await pin('neutral/volume-slices.json', slices) }), /slab interval|layer plan|samples differ/);
+ Object.assign(slices, before);
+ }
+});
+
test('retained material rejects changed pinned alpha, positions and slab metadata', async t => {
const { options, slices, pin } = await fixture(t);
await assert.rejects(prepareRetainedMaterialBank({ ...options, scene: { ...options.scene, alphaSha256: 'f'.repeat(64) } }), /alpha support/);
diff --git a/labs/nebula/packages/lab/src/server/workflows/observations/fixtures/omega-centauri-native-stars.json b/labs/nebula/packages/lab/src/server/workflows/observations/fixtures/omega-centauri-native-stars.json
new file mode 100644
index 0000000000..93297884c0
--- /dev/null
+++ b/labs/nebula/packages/lab/src/server/workflows/observations/fixtures/omega-centauri-native-stars.json
@@ -0,0 +1,494 @@
+{
+ "schema": "cssearth-native-registration-regression@1",
+ "source": {
+ "id": "eso0844a",
+ "sha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429"
+ },
+ "reference": {
+ "id": "eso1119b",
+ "sha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6"
+ },
+ "method": "One automatic native matched-star pair per occupied9x9 source cell, closest to cell centre; selected only for spatial coverage. Full1899-pair registration is reproduced by verify-registration.mts. Pixel-edge coordinates, never hand-authored or fitted centroids.",
+ "stars": [
+ {
+ "source": [
+ 7543.851779902359,
+ 7239.617847857838
+ ],
+ "reference": [
+ 11322.15281229721,
+ 11634.175456683972
+ ]
+ },
+ {
+ "source": [
+ 5687.686137602179,
+ 436.7215006734107
+ ],
+ "reference": [
+ 9219.820049167927,
+ 3925.211580937973
+ ]
+ },
+ {
+ "source": [
+ 2261.4968421052636,
+ 426.87461341468537
+ ],
+ "reference": [
+ 5336.417344117647,
+ 3913.7011519607845
+ ]
+ },
+ {
+ "source": [
+ 1347.898671875,
+ 431.6582524594523
+ ],
+ "reference": [
+ 4300.853888772078,
+ 3919.3317483179135
+ ]
+ },
+ {
+ "source": [
+ 7630.445676211454,
+ 2084.808269897288
+ ],
+ "reference": [
+ 11421.73796875,
+ 5793.263458207832
+ ]
+ },
+ {
+ "source": [
+ 7422.353305369127,
+ 5365.482780711255
+ ],
+ "reference": [
+ 11185.553562454481,
+ 9511.6175300437
+ ]
+ },
+ {
+ "source": [
+ 6742.279668141593,
+ 398.34889045231336
+ ],
+ "reference": [
+ 10415.158650487409,
+ 3881.962224817222
+ ]
+ },
+ {
+ "source": [
+ 7626.310823699422,
+ 400.7571624199074
+ ],
+ "reference": [
+ 11417.220290957015,
+ 3884.696935320357
+ ]
+ },
+ {
+ "source": [
+ 2985.3189727420668,
+ 266.78892768897697
+ ],
+ "reference": [
+ 6157.077151754385,
+ 3731.7656131578947
+ ]
+ },
+ {
+ "source": [
+ 503.05705470737917,
+ 1281.8814023393973
+ ],
+ "reference": [
+ 3343.541030732044,
+ 4882.500369475138
+ ]
+ },
+ {
+ "source": [
+ 7561.378741513094,
+ 1285.936544106895
+ ],
+ "reference": [
+ 11343.845894292604,
+ 4888.006999598071
+ ]
+ },
+ {
+ "source": [
+ 6764.3333815427,
+ 1259.7162043680137
+ ],
+ "reference": [
+ 10440.06060458787,
+ 4858.455919517885
+ ]
+ },
+ {
+ "source": [
+ 6841.531795175023,
+ 6273.889729200526
+ ],
+ "reference": [
+ 10527.016888041733,
+ 10540.675852728731
+ ]
+ },
+ {
+ "source": [
+ 7437.476032731377,
+ 6472.286986014838
+ ],
+ "reference": [
+ 11202.760412888601,
+ 10765.861193329016
+ ]
+ },
+ {
+ "source": [
+ 6675.76548053528,
+ 7099.297030413949
+ ],
+ "reference": [
+ 10338.940481157468,
+ 11476.379925975776
+ ]
+ },
+ {
+ "source": [
+ 522.0218641868513,
+ 490.8904491461724
+ ],
+ "reference": [
+ 3364.7552612665113,
+ 3986.194804778555
+ ]
+ },
+ {
+ "source": [
+ 4754.070043436293,
+ 354.5155471877089
+ ],
+ "reference": [
+ 8161.646314464143,
+ 3832.487265310234
+ ]
+ },
+ {
+ "source": [
+ 2255.604497635934,
+ 1254.8404962464715
+ ],
+ "reference": [
+ 5329.904199626866,
+ 4852.031908582089
+ ]
+ },
+ {
+ "source": [
+ 483.2403293918919,
+ 2094.911736384084
+ ],
+ "reference": [
+ 3321.311634063173,
+ 5804.353256460875
+ ]
+ },
+ {
+ "source": [
+ 7582.382096188749,
+ 2894.94514095616
+ ],
+ "reference": [
+ 11367.308461382114,
+ 6711.5827296747975
+ ]
+ },
+ {
+ "source": [
+ 7593.414093904448,
+ 4656.9400248014945
+ ],
+ "reference": [
+ 11379.783823891625,
+ 8708.66892065447
+ ]
+ },
+ {
+ "source": [
+ 1323.1490073529412,
+ 5539.345107293117
+ ],
+ "reference": [
+ 4273.514848971723,
+ 9708.254120608399
+ ]
+ },
+ {
+ "source": [
+ 501.0830254378649,
+ 6278.957477859219
+ ],
+ "reference": [
+ 3341.0643057620814,
+ 10546.047803903344
+ ]
+ },
+ {
+ "source": [
+ 1217.749043062201,
+ 6256.017360240901
+ ],
+ "reference": [
+ 4153.9168594968105,
+ 10520.531769135365
+ ]
+ },
+ {
+ "source": [
+ 4139.995373201438,
+ 370.2603777121882
+ ],
+ "reference": [
+ 7465.557692383583,
+ 3849.709580702447
+ ]
+ },
+ {
+ "source": [
+ 1380.4030522001726,
+ 1235.5863743029079
+ ],
+ "reference": [
+ 4337.925964928732,
+ 4830.4725553263315
+ ]
+ },
+ {
+ "source": [
+ 417.61271794871794,
+ 2874.0462642914117
+ ],
+ "reference": [
+ 3246.69359915379,
+ 6687.630337564386
+ ]
+ },
+ {
+ "source": [
+ 470.95762676056336,
+ 3766.3383726983006
+ ],
+ "reference": [
+ 3307.2976515459723,
+ 7698.223851708705
+ ]
+ },
+ {
+ "source": [
+ 7717.086303905614,
+ 3776.441429523033
+ ],
+ "reference": [
+ 11520.341135703917,
+ 7710.460177793904
+ ]
+ },
+ {
+ "source": [
+ 415.74783972125437,
+ 5471.929494620657
+ ],
+ "reference": [
+ 3244.8621814814815,
+ 9631.635266666666
+ ]
+ },
+ {
+ "source": [
+ 6538.877946859902,
+ 5574.349328925008
+ ],
+ "reference": [
+ 10184.229849662162,
+ 9747.699506756757
+ ]
+ },
+ {
+ "source": [
+ 1395.560259391771,
+ 7146.290813494488
+ ],
+ "reference": [
+ 4355.044539368506,
+ 11529.267664868104
+ ]
+ },
+ {
+ "source": [
+ 2264.8153571428575,
+ 6995.644730260164
+ ],
+ "reference": [
+ 5340.305351906159,
+ 11358.313015029325
+ ]
+ },
+ {
+ "source": [
+ 3019.652123115578,
+ 7207.609798527335
+ ],
+ "reference": [
+ 6195.522489114659,
+ 11598.123013425253
+ ]
+ },
+ {
+ "source": [
+ 6197.32899825784,
+ 1070.7190290594742
+ ],
+ "reference": [
+ 9797.298086283186,
+ 4644.245955014749
+ ]
+ },
+ {
+ "source": [
+ 6755.3925,
+ 2038.6018326342232
+ ],
+ "reference": [
+ 10430.000545897079,
+ 5741.060354659249
+ ]
+ },
+ {
+ "source": [
+ 1193.7030412844038,
+ 1895.1709456542153
+ ],
+ "reference": [
+ 4126.50989280245,
+ 5578.658993108729
+ ]
+ },
+ {
+ "source": [
+ 491.012919550173,
+ 4638.71334282184
+ ],
+ "reference": [
+ 3330.162420694864,
+ 8686.934805513594
+ ]
+ },
+ {
+ "source": [
+ 2017.3430107973422,
+ 6191.704550373169
+ ],
+ "reference": [
+ 5060.271005492558,
+ 10447.771228738484
+ ]
+ },
+ {
+ "source": [
+ 422.7730141388174,
+ 7139.327238216058
+ ],
+ "reference": [
+ 3252.443440944882,
+ 11521.285627952757
+ ]
+ },
+ {
+ "source": [
+ 4758.961025086505,
+ 6836.993869884785
+ ],
+ "reference": [
+ 8166.450460620101,
+ 11178.59463560228
+ ]
+ },
+ {
+ "source": [
+ 5527.9480066666665,
+ 7048.0824248869985
+ ],
+ "reference": [
+ 9037.86875,
+ 11417.925
+ ]
+ },
+ {
+ "source": [
+ 3805.70448019802,
+ 7060.095337505035
+ ],
+ "reference": [
+ 7087.043556034483,
+ 11430.928817970822
+ ]
+ },
+ {
+ "source": [
+ 919.8742317708334,
+ 4073.837202539218
+ ],
+ "reference": [
+ 3815.942778324154,
+ 8046.978390047207
+ ]
+ },
+ {
+ "source": [
+ 7093.627315668203,
+ 4927.450403547444
+ ],
+ "reference": [
+ 10812.745688613231,
+ 9015.649785305344
+ ]
+ },
+ {
+ "source": [
+ 2694.656476555455,
+ 841.9393643988454
+ ],
+ "reference": [
+ 5827.302483305509,
+ 4384.347057595994
+ ]
+ },
+ {
+ "source": [
+ 4909.985445945946,
+ 6673.914600056052
+ ],
+ "reference": [
+ 8337.908433972912,
+ 10993.507053235517
+ ]
+ },
+ {
+ "source": [
+ 3019.2214808612443,
+ 6705.13800284715
+ ],
+ "reference": [
+ 6194.89690607172,
+ 11028.996604523227
+ ]
+ }
+ ]
+}
diff --git a/labs/nebula/packages/lab/src/server/workflows/observations/registration.test.ts b/labs/nebula/packages/lab/src/server/workflows/observations/registration.test.ts
index 9ce226da93..2279c7329e 100644
--- a/labs/nebula/packages/lab/src/server/workflows/observations/registration.test.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/observations/registration.test.ts
@@ -17,6 +17,38 @@ test('native AVM reference pixel, not raster centre, anchors the sky coordinates
assert.ok(Math.hypot(recovered[0] - 100, recovered[1] - 200) < 1e-8);
});
+test('Omega Centauri native stars reject the former centred AVM reference pixels', async () => {
+ const recipe = readObservationRecipe(JSON.parse(await readFile('labs/nebula/models/omega-centauri/observations.json', 'utf8')));
+ const value: unknown = JSON.parse(await readFile('labs/nebula/packages/lab/src/server/workflows/observations/fixtures/omega-centauri-native-stars.json', 'utf8'));
+ assert.ok(value && typeof value === 'object' && 'stars' in value && Array.isArray(value.stars));
+ const reference = recipe.images.find(image => image.id === 'eso1119b')!, source = recipe.images.find(image => image.id === 'eso0844a')!;
+ assert.ok('source' in value && 'reference' in value);
+ assert.deepEqual(value.source, { id: source.id, sha256: source.sha256 });
+ assert.deepEqual(value.reference, { id: reference.id, sha256: reference.sha256 });
+ const point = (input: unknown): [number, number] => {
+ assert.ok(Array.isArray(input) && input.length === 2 && input.every(n => typeof n === 'number' && Number.isFinite(n)));
+ return [input[0], input[1]];
+ };
+ const referenceMatrix = publisherTransform(reference, recipe.frame);
+ const pairs: Pair[] = value.stars.map((row: unknown, index: number) => {
+ assert.ok(row && typeof row === 'object' && 'source' in row && 'reference' in row);
+ return { source: point(row.source), frame: applyAffine(referenceMatrix, point(row.reference)), sourceIndex: index, referenceIndex: index };
+ });
+ const footprint = { width: reference.width, height: reference.height, imageToFrame: referenceMatrix };
+ const verified = verifyRegistration(pairs, source, recipe.frame, publisherTransform(source, recipe.frame), footprint);
+ assert.equal(verified.pass, true);
+ assert.ok(verified.evidence.residualArcseconds < .2);
+ const centred = structuredClone(source);
+ assert.ok(centred.wcs);
+ centred.wcs.referencePixel = [centred.wcs.referenceDimension[0] / 2, centred.wcs.referenceDimension[1] / 2];
+ assert.equal(verifyRegistration(pairs, centred, recipe.frame, publisherTransform(centred, recipe.frame), footprint).pass, false,
+ 'Replacing the publisher reference pixel with the raster centre destroys the qualified native alignment.');
+ const corrupted = structuredClone(pairs);
+ const heldOut = verified.evidence.matches.find(match => match.heldOut)!;
+ corrupted.find(pair => pair.source[0] === heldOut.source[0] && pair.source[1] === heldOut.source[1])!.frame[0] += 3;
+ assert.equal(verifyRegistration(corrupted, source, recipe.frame, publisherTransform(source, recipe.frame), footprint).pass, false);
+});
+
test('Gaussian high-pass keeps channels aligned and finds actual compact sources', async () => {
const width = 128, height = 96, raster = Buffer.alloc(width * height * 3);
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
diff --git a/labs/nebula/packages/lab/src/server/workflows/sampled-prior/compile.ts b/labs/nebula/packages/lab/src/server/workflows/sampled-prior/compile.ts
index 86ac56a414..fa7fa57f98 100644
--- a/labs/nebula/packages/lab/src/server/workflows/sampled-prior/compile.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/sampled-prior/compile.ts
@@ -23,6 +23,7 @@ import { registerComponentBanks } from './layout.ts';
import { fitSampledEmission, type EmissionFitResult } from '@cssearth/nebula-reconstruction/methods/sampled/emission-fit';
import { prepareSampledMaterial } from '@cssearth/volume-core/materials/sampled';
import { fitSampledMaterialColors } from '@cssearth/nebula-reconstruction/methods/sampled/material-fit';
+import { maximumPlanningEmission } from '@cssearth/volume-bake/compact-inputs/sampled';
/** Spectral appearances belong to the final scene, so its atlas must include their complete palette. */
export async function prepareSampledSceneStars(root: string, outputDirectory: string,
@@ -169,6 +170,10 @@ export async function compileSampledNebula(root: string, request: CompilerReques
...prepared.evidence, source: sampled.source, rawToArcsec: sampled.rawToArcsec, terms: sampled.terms, ejecta, wind, emissionFits })));
const referenceFit = fitsBySource.get(reference.id);
const neutralField = referenceFit ? prepared.field({ ejecta: referenceFit.receipt.ejectaGain, pwn: 1 }, 1, referenceFit.diffuse) : field;
+ const mixtureFields = new Map(sourceData.images.map(image => [image.id,
+ fitsBySource.get(image.id)?.field ?? prepared.field(sampled.lensComponents[image.id]!)]));
+ const samplePlanningEmission = maximumPlanningEmission([neutralField.sampleEmission,
+ ...sourceData.images.map(image => mixtureFields.get(image.id)!.sampleEmission)]);
started = performance.now();
// Independent radiative components share a frame, never an inferred photo extrusion.
// Within one mixture the normal baker enforces exact shared alpha for its RGB materials.
@@ -178,6 +183,7 @@ export async function compileSampledNebula(root: string, request: CompilerReques
const group = groups.get(key) ?? []; group.push(image); groups.set(key, group);
}
const neutral = await bakeCompiler({ root, outputDirectory: `${directory}/scene-neutral`, id, fieldIdentity, boundsArcsec: field.bounds,
+ samplePlanningEmission,
skyBoundsArcsec: skyBounds, sampleEmission: neutralField.sampleEmission, lenses: [{ id: 'neutral-material', label: 'Neutral components', sampleMaterial(_x, _y, _z, rgb) { rgb.fill(255); return true; } }],
stars: stars.map(({ materials: _materials, ...star }) => star), signal,
progress: p => progress(`Neutral components · ${p.message}`, .3 + .08 * sampledBakeProgress(p)) });
@@ -185,7 +191,7 @@ export async function compileSampledNebula(root: string, request: CompilerReques
fit?: ReturnType['receipt'] }[] = []; let groupIndex = 0;
let referenceMaterial: ReturnType['sampleMaterial'] | undefined;
for (const images of groups.values()) {
- const mixture = fitsBySource.get(images[0]!.id)?.field ?? prepared.field(sampled.lensComponents[images[0]!.id]!);
+ const mixture = mixtureFields.get(images[0]!.id)!;
const materials = images.map(image => {
progress(`Assigning ${image.label} colors to finite 3D emitters…`, .38 + .5 * groupIndex / groups.size);
const fit = fitsBySource.get(image.id);
@@ -198,6 +204,7 @@ export async function compileSampledNebula(root: string, request: CompilerReques
return { id: image.id, label: image.label, sampleMaterial: material.sampleMaterial };
});
const bank = await bakeCompiler({ root, outputDirectory: `${directory}/scene-${groupIndex}`, id, fieldIdentity,
+ sampling: neutral.sampling,
boundsArcsec: field.bounds, skyBoundsArcsec: skyBounds, sampleEmission: mixture.sampleEmission,
lenses: materials, signal,
progress: p => progress(`${images.map(i => i.label).join(' / ')} · ${p.message}`, .4 + .5 * (groupIndex + sampledBakeProgress(p)) / groups.size) });
diff --git a/labs/nebula/packages/lab/src/server/workflows/sampled-prior/component-banks.test.ts b/labs/nebula/packages/lab/src/server/workflows/sampled-prior/component-banks.test.ts
new file mode 100644
index 0000000000..35f4af9841
--- /dev/null
+++ b/labs/nebula/packages/lab/src/server/workflows/sampled-prior/component-banks.test.ts
@@ -0,0 +1,85 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+import sharp from 'sharp';
+import { bakeMasterVolumeSlices } from '@cssearth/volume-bake/slices/emission';
+import { compilerAlphaDigest } from '@cssearth/volume-bake/compiler/bake';
+import { compilerFrame, compilerPreparedSlices } from '@cssearth/volume-core/coordinates/compiler-frame';
+import { readCompilerBakeResult, type CompilerPin } from '@cssearth/volume-core/contracts/compiler-bake';
+import { validateVolumeLayerSlices, type VolumeLayerPlan } from '@cssearth/volume-core/contracts/volume-slices';
+import { sha256, verifiedBytes } from '@cssearth/volume-bake/compact-inputs/density-grid';
+import { registerComponentBanks } from '@cssearth/volume-bake/compiler/component-layout';
+import { compileCssVolume } from '../../../adapters/preparation/css-volume.ts';
+import { validatePreparedCssVolume } from '../../../adapters/renderer/volume-validation.ts';
+
+async function fixture(t: { after(fn: () => Promise): void }) {
+ const root = await mkdtemp(join(tmpdir(), 'registered-empty-layers-'));
+ t.after(() => rm(root, { recursive: true, force: true }));
+ const boundsArcsec = { min: [-2, -2, -2] as [number, number, number], max: [2, 2, 2] as [number, number, number] };
+ const { frame, localBounds, origin } = compilerFrame(boundsArcsec, true);
+ const groups = () => [{ startCell: 0, endCell: 1 }, { startCell: 1, endCell: 3 }, { startCell: 3, endCell: 4 }];
+ const layerPlan: VolumeLayerPlan = { schema: 'cssearth-volume-layer-plan@1', referenceSliceCounts: { x: 4, y: 4, z: 4 },
+ referenceSamplesPerSlab: 4, axes: { x: groups(), y: groups(), z: groups() } };
+ const { banks } = await bakeMasterVolumeSlices({ boundsKpc: localBounds, sliceCounts: { x: 3, y: 3, z: 3 }, samplesPerSlab: 4, layerPlan,
+ exposureGain: 1, masterWidth: 8, masterDirectory: join(root, 'masters'), unitsPerSourceUnit: 1, provenance: {}, cropTransparent: true,
+ deliveryBanks: [{ width: 8, outputDirectory: join(root, 'neutral'), imageEncoding: { format: 'png' } }],
+ sampleEmission(x, y, z, out) { out.fill(Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) < .6 ? .2 : 0); }, onProgress() {} });
+ const slices = banks[0]!.slices, fieldIdentity = 'a'.repeat(64), alphaSha256 = await compilerAlphaDigest(join(root, 'neutral'), slices);
+ slices.provenance = { fieldIdentity, alphaSha256 };
+ await writeFile(join(root, 'neutral/volume-slices.json'), JSON.stringify(slices));
+ const volume = compileCssVolume({ id: 'compiler-empty-layers', frame, slices: compilerPreparedSlices(slices), recipe: { anchors: [] } });
+ const bytes = Buffer.from(JSON.stringify(volume));
+ await writeFile(join(root, 'neutral/volume.json'), bytes);
+ const neutral: CompilerPin = { path: 'neutral/volume.json', sha256: sha256(bytes) };
+ const scene = readCompilerBakeResult({ schema: 'cssearth-compiler-bake@1', id: 'empty-layers', fieldIdentity, frame, boundsArcsec,
+ skyBoundsArcsec: { min: [-2, -2], max: [2, 2] }, spanArcsec: 4, sourceImage: { width: 512, height: 512 },
+ coordinates: { axes: ['west', 'north', 'away'], localOriginArcsec: origin, earthView: 'observer-at-negative-z-looking-away' }, neutral, alphaSha256,
+ stars: [], lenses: [{ id: 'optical', label: 'Optical', volume: neutral, coverage: { positiveAlphaTexels: 12, recoloredTexels: 12, outsideImageTexels: 0 } }],
+ sampling: { sliceCounts: { x: 3, y: 3, z: 3 }, imageWidth: 512, samplesPerSlab: 4, layerPlan } });
+ const readPinned = (pin: CompilerPin) => verifiedBytes(root, pin);
+ return { root, scene, slices, readPinned };
+}
+
+test('registration preserves the full planned partition until compilation prunes truly empty slabs', async t => {
+ const { root, scene, slices, readPinned } = await fixture(t), empty = slices.quads.filter(q => q.alphaCoverage === 0);
+ assert.equal(empty.length, 6);
+ let compiled = 0;
+ const result = await registerComponentBanks(root, 'registered', scene, scene.lenses, new AbortController().signal, readPinned, {
+ compileVolume(input) {
+ compiled++;
+ assert.equal(input.slices.quads.length, 9);
+ assert.ok(validateVolumeLayerSlices(input.slices));
+ assert.deepEqual(input.slices.quads.filter(q => q.alphaCoverage === 0).map(q => q.id).sort(), empty.map(q => q.id).sort());
+ return compileCssVolume({ ...input, recipe: { anchors: [] } });
+ }, validateVolume: validatePreparedCssVolume,
+ });
+ assert.equal(compiled, 1);
+ for (const pin of [result.neutral, result.lenses[0]!.volume]) {
+ const volume = validatePreparedCssVolume(JSON.parse((await readPinned(pin)).toString()));
+ assert.equal(volume.resources.length, 3, 'Empty preparation-only quads must never create runtime resources.');
+ assert.deepEqual(volume.stacks.flatMap(s => s.leaves.map(q => q.id)).sort(), ['x-1', 'y-1', 'z-1']);
+ assert.ok(volume.resources.every(r => !empty.some(q => q.texturePath === r.path)));
+ }
+});
+
+test('component registration rejects catalogue intervals that disagree with the actual retained plan', async t => {
+ const { root, scene, slices, readPinned } = await fixture(t);
+ slices.quads[0]!.slab!.end += .125;
+ await writeFile(join(root, 'neutral/volume-slices.json'), JSON.stringify(slices));
+ await assert.rejects(registerComponentBanks(root, 'registered', scene, scene.lenses, new AbortController().signal, readPinned, {
+ compileVolume() { throw new Error('Invalid catalogue must fail before compile'); }, validateVolume: validatePreparedCssVolume,
+ }), /Physical slab interval/);
+});
+
+test('an allegedly pruned slab with real opacity cannot be reintroduced as transparent metadata', async t => {
+ const { root, scene, slices, readPinned } = await fixture(t), q = slices.quads.find(q => q.alphaCoverage === 0)!;
+ const pixels = Buffer.alloc(q.widthPx * q.heightPx * 4); pixels[3] = 255;
+ const bytes = await sharp(pixels, { raw: { width: q.widthPx, height: q.heightPx, channels: 4 } }).png().toBuffer();
+ await writeFile(join(root, 'neutral', q.texturePath), bytes); q.sha256 = sha256(bytes); q.bytes = bytes.length;
+ await writeFile(join(root, 'neutral/volume-slices.json'), JSON.stringify(slices));
+ await assert.rejects(registerComponentBanks(root, 'registered', scene, scene.lenses, new AbortController().signal, readPinned, {
+ compileVolume() { throw new Error('Nontransparent omitted slab must fail before compile'); }, validateVolume: validatePreparedCssVolume,
+ }), /omitted.*opacity/i);
+});
diff --git a/labs/nebula/packages/lab/src/server/workflows/sampled-prior/layout.test.ts b/labs/nebula/packages/lab/src/server/workflows/sampled-prior/layout.test.ts
index c66d1b3dc0..f8957b73b7 100644
--- a/labs/nebula/packages/lab/src/server/workflows/sampled-prior/layout.test.ts
+++ b/labs/nebula/packages/lab/src/server/workflows/sampled-prior/layout.test.ts
@@ -106,3 +106,13 @@ test('footprint union cannot merge different slabs, resampled pixels or offsets
quad('z-7', 2, 3, [11, 21, 30.1], u, v),
]) assert.throws(() => mergeSliceLayout(reference, [component]), /registered pixel grid/);
});
+
+test('registered footprint union retains physical slab intervals and rejects mismatched integration support', () => {
+ const reference = quad('z-7', 6, 5, origin, [1, 0, 0], [0, 1, 0]);
+ reference.slab = { start: 29, end: 31, samples: 8, startCell: 2, endCell: 4 };
+ assert.deepEqual(mergeSliceLayout(reference, [structuredClone(reference)]).slab, reference.slab);
+ const changed = structuredClone(reference); changed.slab!.samples = 4;
+ assert.throws(() => mergeSliceLayout(reference, [changed]), /different physical integration/);
+ delete changed.slab;
+ assert.throws(() => mergeSliceLayout(reference, [changed]), /different physical integration/);
+});
diff --git a/labs/nebula/packages/lab/src/state/subjects.json b/labs/nebula/packages/lab/src/state/subjects.json
index 95b8a3cb99..a8ec263c15 100644
--- a/labs/nebula/packages/lab/src/state/subjects.json
+++ b/labs/nebula/packages/lab/src/state/subjects.json
@@ -408,6 +408,25 @@
"statusNote": "Published constraints guide authored depth; this is not measured gas density."
}
},
+ {
+ "id": "omega-centauri",
+ "name": "Omega Centauri",
+ "menuLabel": "Omega Centauri",
+ "workflow": "inference",
+ "directory": ".local/nebula-lab/compiler-published/omega-centauri",
+ "hasDetail": false,
+ "modelNote": "Published oblate starlight prior; finite feature depths remain conditional, not measured star positions.",
+ "observationAlignment": {
+ "manifest": ".local/nebula-lab/observations/omega-centauri/observations.json",
+ "recipe": "labs/nebula/models/omega-centauri/observations.json"
+ },
+ "emissionExperiment": {
+ "observationStructures": ".local/nebula-lab/observations/omega-centauri/structures/catalogue.json",
+ "compilerSource": "labs/nebula/models/omega-centauri/compiler.json",
+ "compilerPublished": ".local/nebula-lab/compiler-published/omega-centauri.json",
+ "statusNote": "Published oblate prior with conditional finite feature depths; integrated stellar light is preserved."
+ }
+ },
{
"id": "m1",
"name": "M1 · Crab",
diff --git a/labs/nebula/packages/reconstruction/package.json b/labs/nebula/packages/reconstruction/package.json
index a1c61142f7..10574ead4a 100644
--- a/labs/nebula/packages/reconstruction/package.json
+++ b/labs/nebula/packages/reconstruction/package.json
@@ -20,6 +20,7 @@
"./methods/sampled/emission-detail": "./src/methods/sampled/emission-detail.ts",
"./methods/sampled/emission-fit": "./src/methods/sampled/emission-fit.ts",
"./methods/inference/depth-model": "./src/methods/inference/depth-model.ts",
+ "./methods/inference/photometric-mge": "./src/methods/inference/photometric-mge.ts",
"./methods/inference/fit": "./src/methods/inference/fit.ts",
"./evidence/model": "./src/evidence/model.ts",
"./methods/inference/target": "./src/methods/inference/target.ts",
diff --git a/labs/nebula/packages/reconstruction/src/methods/inference/fit.ts b/labs/nebula/packages/reconstruction/src/methods/inference/fit.ts
index f344d40f3a..c922a91158 100644
--- a/labs/nebula/packages/reconstruction/src/methods/inference/fit.ts
+++ b/labs/nebula/packages/reconstruction/src/methods/inference/fit.ts
@@ -103,14 +103,16 @@ function buildComponents(bases: Basis[], input: EmissionFitInput, controls: Comp
}
/** Fit only the supplied target. Image colors, source selection and stellar overlays never enter this field. */
export function fitEmissionField(input: EmissionFitInput, requested: unknown = defaultCompilerControls,
- options: { signal?: AbortSignal; onProgress?(message: string): void; depthRecipe?: DepthRecipe; externalDepthAssignment?: boolean } = {}): EmissionFitResult {
+ options: { signal?: AbortSignal; onProgress?(message: string): void; depthRecipe?: DepthRecipe; externalDepthAssignment?: boolean; maximumComponents?: number } = {}): EmissionFitResult {
validateInput(input); const controls = readCompilerControls(requested), grid = fitGrid(input, controls.detail);
const depthRecipe = options.depthRecipe && readDepthRecipe(options.depthRecipe);
if (depthRecipe && input.scaffold) throw new TypeError('Choose a surface depth model or a joint velocity scaffold; do not silently combine incompatible depth methods.');
if (options.externalDepthAssignment && (depthRecipe || input.scaffold)) throw new TypeError('External density conditioning cannot combine with a surface or velocity scaffold.');
const externallyConditioned = Boolean(depthRecipe || options.externalDepthAssignment);
const residual = Float64Array.from(grid.target), bases: Basis[] = [], blocked = new Uint8Array(grid.target.length);
- const componentBudget = Math.round(144 + 336 * controls.detail), cutoff = grid.target.reduce((a, b) => Math.max(a, b), 0) * (.004 + .056 * (1 - controls.faint));
+ if (options.maximumComponents !== undefined && (!Number.isInteger(options.maximumComponents) || options.maximumComponents < 16 || options.maximumComponents > 8192))
+ throw new TypeError('Finite component budget must be an integer between 16 and 8192.');
+ const componentBudget = options.maximumComponents ?? Math.round(144 + 336 * controls.detail), cutoff = grid.target.reduce((a, b) => Math.max(a, b), 0) * (.004 + .056 * (1 - controls.faint));
let outerRadius = 0;
for (let p = 0; p < grid.target.length; p++) if (grid.coverage[p] && grid.target[p] > cutoff) {
const x = input.bounds.min[0] + (p % grid.width + .5) * grid.dx, y = input.bounds.max[1] - (Math.floor(p / grid.width) + .5) * grid.dy;
diff --git a/labs/nebula/packages/reconstruction/src/methods/inference/photometric-mge.test.ts b/labs/nebula/packages/reconstruction/src/methods/inference/photometric-mge.test.ts
new file mode 100644
index 0000000000..090e4f5a84
--- /dev/null
+++ b/labs/nebula/packages/reconstruction/src/methods/inference/photometric-mge.test.ts
@@ -0,0 +1,74 @@
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { createPhotometricMgePrior, readPhotometricMgeRecipe, type PhotometricMgeRecipe } from './photometric-mge.ts';
+import { conditionSimulationComponents, type SimulationDepthSettings } from './simulation-guided.ts';
+import { projectEmissionComponent } from '@cssearth/volume-core/fields/emission';
+import type { EmissionComponent } from '@cssearth/volume-core/contracts/emission';
+
+const recipe: PhotometricMgeRecipe = {
+ schema: 'cssearth-photometric-mge@1', id: 'fixture', centerIcrsDegrees: [201, -47], distancePc: 5426,
+ positionAngleEastOfNorthDegrees: 0, inclinationDegrees: 60, lineOfSightTiltSign: 1, cutoffSigma: 6,
+ gaussians: [{ centralAmplitude: 4, sigmaArcsec: 20, projectedAxisRatio: .8 }],
+ evidence: { path: 'labs/nebula/models/fixture/evidence.json', sha256: 'b'.repeat(64) },
+ source: { url: 'https://example.org/observations', sha256: 'c'.repeat(64), locator: 'test fixture' },
+ interpretation: 'Oblate deprojection test, not an observed object.',
+};
+function column(model: PhotometricMgeRecipe, x: number, y: number) {
+ const prior = createPhotometricMgePrior(model), n = 4096, dz = (prior.bounds.max[2] - prior.bounds.min[2]) / n;
+ let sum = 0;
+ for (let i = 0; i < n; i++) sum += prior.sampleDensity(x, y, prior.bounds.min[2] + (i + .5) * dz) * dz;
+ return sum;
+}
+
+test('deprojection integrates back to the published projected ellipse at different inclinations', () => {
+ for (const inclinationDegrees of [45, 60, 85]) {
+ const model = { ...recipe, inclinationDegrees };
+ assert.ok(Math.abs(column(model, 0, 0) - 1) < 1e-6);
+ // PA=0 is north-south; west is the shorter projected axis.
+ assert.ok(Math.abs(column(model, 0, 20) - Math.exp(-.5)) < 1e-6);
+ assert.ok(Math.abs(column(model, 20, 0) - Math.exp(-.5 / .8 ** 2)) < 1e-6);
+ }
+ assert.ok(Math.abs(column({ ...recipe, positionAngleEastOfNorthDegrees: 90 }, 20, 0) - Math.exp(-.5)) < 1e-6);
+});
+
+test('line-of-sight mirror is an explicit degeneracy and source pins participate in identity', () => {
+ const first = createPhotometricMgePrior(recipe), mirror = createPhotometricMgePrior({ ...recipe, lineOfSightTiltSign: -1 });
+ assert.equal(first.sampleDensity(10, 7, 13), mirror.sampleDensity(10, 7, -13));
+ assert.notEqual(first.identity, mirror.identity);
+ assert.notEqual(first.identity, createPhotometricMgePrior({ ...recipe, source: { ...recipe.source, sha256: 'd'.repeat(64) } }).identity);
+ assert.throws(() => readPhotometricMgeRecipe({ ...recipe, inclinationDegrees: 20 }), /cannot be deprojected/);
+ assert.throws(() => readPhotometricMgeRecipe({ ...recipe, lineOfSightTiltSign: '1' }), /Invalid/);
+});
+
+test('conditional finite features fill the prior depth distribution instead of collapsing into its modal plane', () => {
+ const prior = createPhotometricMgePrior({ ...recipe, gaussians: [{ centralAmplitude: 1, sigmaArcsec: 20, projectedAxisRatio: 1 }] });
+ const components: EmissionComponent[] = Array.from({ length: 500 }, (_, i) => ({
+ id: `feature-${i}`, basisId: 'fixture', center: [0, 0, 0], sigma: [1, 1, 1], angleRadians: 0,
+ projectedWeight: 1, depthAssignment: 'halo-diffuse', velocityCovered: false,
+ }));
+ const settings: SimulationDepthSettings = { depthSamples: 2048, modeRelativeThreshold: .1, maximumModes: 1,
+ minimumSigmaZ: .5, maximumSigmaZ: 2, featureThicknessRatio: 1, supportSigma: 4, placement: 'conditional-quantile' };
+ const result = conditionSimulationComponents(components, prior, settings);
+ assert.equal(result.components.length, components.length, 'Each feature remains one finite emitter.');
+ const depths = result.components.map(component => component.center[2]);
+ const mean = depths.reduce((sum, z) => sum + z, 0) / depths.length;
+ const rms = Math.sqrt(depths.reduce((sum, z) => sum + (z - mean) ** 2, 0) / depths.length);
+ assert.ok(Math.abs(mean) < 2, `Conditional mean ${mean}`);
+ assert.ok(rms > 18 && rms < 22, `Conditional standard deviation ${rms}`);
+ assert.ok(depths.some(z => z < -40) && depths.some(z => z > 40));
+ const brighter = conditionSimulationComponents(components.map(component => ({ ...component, projectedWeight: 9 })), prior, settings);
+ assert.deepEqual(brighter.components.map(component => component.center), result.components.map(component => component.center));
+ for (let i = 0; i < components.length; i++)
+ assert.equal(projectEmissionComponent(result.components[i]!, .4, .2), projectEmissionComponent(components[i]!, .4, .2));
+});
+
+test('conditional draws reject unsupported light and thick boundary kernels instead of making midplane clumps', () => {
+ const component: EmissionComponent = { id: 'thick-feature', basisId: 'fixture', center: [0, 0, 0],
+ sigma: [10, 10, 10], angleRadians: 0, projectedWeight: 1, depthAssignment: 'halo-diffuse', velocityCovered: false };
+ const settings: SimulationDepthSettings = { depthSamples: 64, modeRelativeThreshold: .1, maximumModes: 1,
+ minimumSigmaZ: .5, maximumSigmaZ: 10, featureThicknessRatio: 1, supportSigma: 4, placement: 'conditional-quantile' };
+ const bounds = { min: [-100, -100, -100] as [number, number, number], max: [100, 100, 100] as [number, number, number] };
+ assert.throws(() => conditionSimulationComponents([component], { identity: 'a'.repeat(64), bounds, sampleDensity: () => 0 }, settings), /positive prior support/);
+ assert.throws(() => conditionSimulationComponents([component], { identity: 'a'.repeat(64), bounds,
+ sampleDensity: (_x, _y, z) => z > 90 ? 1 : 0 }, settings), /do not clamp/);
+});
diff --git a/labs/nebula/packages/reconstruction/src/methods/inference/photometric-mge.ts b/labs/nebula/packages/reconstruction/src/methods/inference/photometric-mge.ts
new file mode 100644
index 0000000000..8ed0ae5dc7
--- /dev/null
+++ b/labs/nebula/packages/reconstruction/src/methods/inference/photometric-mge.ts
@@ -0,0 +1,8 @@
+/** Fitting adapter: the deterministic density and validation are shared with cold bake replay. */
+import { createHash } from 'node:crypto';
+import { readPhotometricMgeRecipe, samplePhotometricMge, type PhotometricMgeRecipe } from '@cssearth/volume-core/fields/photometric-mge';
+export { readPhotometricMgeRecipe, type PhotometricMgeRecipe, type PhotometricGaussian } from '@cssearth/volume-core/fields/photometric-mge';
+export function createPhotometricMgePrior(recipe: PhotometricMgeRecipe) {
+ const parsed = readPhotometricMgeRecipe(recipe);
+ return { identity: createHash('sha256').update(JSON.stringify(parsed)).digest('hex'), ...samplePhotometricMge(parsed) };
+}
diff --git a/labs/nebula/packages/reconstruction/src/methods/inference/simulation-guided.ts b/labs/nebula/packages/reconstruction/src/methods/inference/simulation-guided.ts
index 30ab6f95ab..4776f21d6c 100644
--- a/labs/nebula/packages/reconstruction/src/methods/inference/simulation-guided.ts
+++ b/labs/nebula/packages/reconstruction/src/methods/inference/simulation-guided.ts
@@ -14,6 +14,8 @@ export interface SimulationDepthPrior {
export interface SimulationDepthSettings {
depthSamples:number;modeRelativeThreshold:number;maximumModes:number;
minimumSigmaZ:number;maximumSigmaZ:number;featureThicknessRatio:number;supportSigma:number;
+ /** A deterministic conditional draw assigns each finite feature one depth; it never repeats the image along a ray. */
+ placement?:'modes'|'conditional-quantile';
}
export interface SimulationDepthAssignment {
sourceComponentId:string;componentIds:string[];supported:boolean;profileMass:number;
@@ -26,7 +28,9 @@ function validate(prior:SimulationDepthPrior,s:SimulationDepthSettings):void {
!Number.isFinite(s.modeRelativeThreshold)||s.modeRelativeThreshold<=0||s.modeRelativeThreshold>1||
!Number.isFinite(s.minimumSigmaZ)||s.minimumSigmaZ<=0||!Number.isFinite(s.maximumSigmaZ)||s.maximumSigmaZprior.bounds.max[2]-prior.bounds.min[2])throw Error('Invalid finite-depth settings; emission kernels have fixed ±4σ support');
+ 8*s.minimumSigmaZ>prior.bounds.max[2]-prior.bounds.min[2]||
+ (s.placement!==undefined&&s.placement!=='modes'&&s.placement!=='conditional-quantile')||
+ (s.placement==='conditional-quantile'&&s.maximumModes!==1))throw Error('Invalid finite-depth settings; emission kernels have fixed ±4σ support');
}
/** Depth probabilities come only from the prior. Image intensity controls projected weight, never depth. */
export function conditionSimulationComponents(components:readonly EmissionComponent[],prior:SimulationDepthPrior,settings:SimulationDepthSettings,signal?:AbortSignal) {
@@ -55,7 +59,17 @@ export function conditionSimulationComponents(components:readonly EmissionCompon
peaks=peaks.sort((a,b)=>smoothed[b]!-smoothed[a]!||a-b).slice(0,settings.maximumModes).sort((a,b)=>a-b);
}
const modes:{center:number;sigma:number;priorWeight:number}[]=[];
- if(peaks.length) {
+ if(mass>0&&settings.placement==='conditional-quantile') {
+ // The salt is the pinned prior, not image intensity or colour. Component IDs identify the fitted support.
+ const quantile=(createHash('sha256').update(prior.identity+':'+component.id).digest().readUInt32BE(0)+.5)/4294967296;
+ let cumulative=0,index=0;
+ for(;index=quantile*mass)break;cumulative+=profile[index]!;}
+ const fraction=profile[index]!>0?(quantile*mass-cumulative)/profile[index]!:0.5;
+ const sigma=Math.min(settings.maximumSigmaZ,Math.max(settings.minimumSigmaZ,Math.min(component.sigma[0],component.sigma[1])*settings.featureThicknessRatio),(zMax-zMin)/8);
+ const center=zMin+(index+fraction)*dz;
+ if(center-4*sigmazMax)throw Error('Conditional depth kernel exceeds the prior bounds; do not clamp its draw');
+ modes.push({center,sigma,priorWeight:1});
+ } else if(peaks.length) {
const boundaries=[0];for(let m=0;m{let sum=0;for(let i=boundaries[m]!;i0?'simulation-prior':'unsupported-local',velocityCovered:false,depthGradient:undefined});
}
- assignments.push({sourceComponentId:component.id,componentIds:ids,supported:peaks.length>0,profileMass:mass*dz,modes});
+ assignments.push({sourceComponentId:component.id,componentIds:ids,supported:mass>0,profileMass:mass*dz,modes});
}
return {components:conditioned,assignments,unsupportedComponents:unsupported,unsupportedComponentFraction:components.length?unsupported/components.length:0,
unsupportedProjectedLightFraction:totalLight>0?unsupportedLight/totalLight:0};
}
export function fitSimulationGuidedEmission(input:EmissionFitInput,requestedControls:unknown,prior:SimulationDepthPrior,settings:SimulationDepthSettings,
- options:{signal?:AbortSignal;onProgress?(message:string):void}={}) {
+ options:{signal?:AbortSignal;onProgress?(message:string):void;maximumComponents?:number}={}) {
validate(prior,settings);const controls=readCompilerControls(requestedControls);
if(controls.depth!==1)throw Error('Simulation-guided depth uses the pinned prior at depth=1; global stretching is not implicit');
const fitted=fitEmissionField(input,controls,{...options,externalDepthAssignment:true});
@@ -94,8 +109,12 @@ export function fitSimulationGuidedEmission(input:EmissionFitInput,requestedCont
const conditioned=conditionSimulationComponents(fitted.field.components,prior,settings,options.signal);
const field:EmissionFieldModel={...fitted.field,components:conditioned.components,identity:'',scaffold:null,
assumptions:{...fitted.field.assumptions,equalNearFarSplit:false,velocityUncoveredComponents:conditioned.components.length,
- depth:'Image-fitted emission weights on finite components; source density supplies conditional ray-depth modes, not gas measurements. Local component thickness is bounded by feature size and prior mode width. With one selected mode, the strongest prior peak receives the entire feature weight as an authored dominant-mode hypothesis. Multiple selected modes receive their normalized basin masses. Image intensity never sets depth.',
- halo:'Unsupported features remain at an explicitly authored local depth with finite feature-sized thickness and are counted separately. No image repetition across a global depth tube.'}};
+ depth:settings.placement==='conditional-quantile'?
+ 'Each finite image feature receives one deterministic conditional depth draw from the pinned density prior. This is an authored realization of a published bulk model, not measured individual stellar depths. Image intensity and colour do not choose the draw; no feature is copied through the line of sight. Finite feature thickness is authored.':
+ 'Image-fitted emission weights on finite components; source density supplies conditional ray-depth modes, not gas measurements. Local component thickness is bounded by feature size and prior mode width. With one selected mode, the strongest prior peak receives the entire feature weight as an authored dominant-mode hypothesis. Multiple selected modes receive their normalized basin masses. Image intensity never sets depth.',
+ halo:settings.placement==='conditional-quantile'?
+ 'Every finite feature requires positive conditional prior support; unsupported features or draws whose kernels exceed the prior bounds reject the fit. No midplane fallback or inward-clamped draws.':
+ 'Unsupported features remain at an explicitly authored local depth with finite feature-sized thickness and are counted separately. No image repetition across a global depth tube.'}};
field.bounds=createEmissionField(field).bounds;
field.identity=createHash('sha256').update(JSON.stringify({field,prior:prior.identity,settings,assignments:conditioned.assignments})).digest('hex');
const prepared=createEmissionField(field);
diff --git a/labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts b/labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts
index 5c7c22513c..fd51ce4228 100644
--- a/labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts
+++ b/labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts
@@ -1,17 +1,18 @@
/** Accepted analytic emission and component colors: no source images, fitting, or baked pixels. */
import assert from 'node:assert/strict';
+import { createHash } from 'node:crypto';
import { dirname } from 'node:path';
import { gunzipSync } from 'node:zlib';
import { bakeCompiler, type CompilerBakeProgress, type CompilerBakeBackend, type CompiledVolumeArtifact } from '../compiler/bake.ts';
import { readCompilerBakeResult, type CompilerBakeResult } from '@cssearth/volume-core/contracts/compiler-bake';
-import { createEmissionField } from '@cssearth/volume-core/fields/emission';
+import { createPhotometricEmission, readEnvelopeColors, type EnvelopeColors } from '@cssearth/volume-core/fields/photometric-emission';
import { readRetainedEmissionField } from '@cssearth/volume-core/fields/retained-emission';
import { pinned, type Pin } from './io.ts';
const record = (v: unknown): v is Record => v !== null && typeof v === 'object' && !Array.isArray(v);
const digest = (v: unknown): v is string => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
const text = (v: unknown): v is string => typeof v === 'string' && v.length > 0;
-interface Material { sourceId: string; components: { id: string; rgb: [number, number, number]; covered: boolean }[] }
+interface Material { sourceId: string; envelopeColors?: EnvelopeColors; components: { id: string; rgb: [number, number, number]; covered: boolean }[] }
interface Resource { path: string; sha256: string; bytes: number; width: number; height: number }
export function readCompactCompiler(value: unknown) {
if (!record(value) || value.schema !== 'cssearth-compact-compiler@1' || !text(value.objectId) ||
@@ -25,7 +26,9 @@ export function readCompactCompiler(value: unknown) {
const materials: Material[] = value.materials.map((material: unknown) => {
if (!record(material) || !text(material.sourceId) || !Array.isArray(material.components) || material.components.length !== componentIds.length)
throw new TypeError('Invalid compact material.');
- return { sourceId: material.sourceId, components: material.components.map((color: unknown, index: number) => {
+ const envelopeColors = field.photometricEnvelope ? readEnvelopeColors(material.envelopeColors, field.photometricEnvelope) : undefined;
+ if (!field.photometricEnvelope && material.envelopeColors !== undefined) throw new TypeError('Envelope material has no retained density.');
+ return { sourceId: material.sourceId, ...(envelopeColors ? { envelopeColors } : {}), components: material.components.map((color: unknown, index: number) => {
if (!record(color) || color.id !== componentIds[index] || typeof color.covered !== 'boolean' || !Array.isArray(color.rgb) ||
color.rgb.length !== 3 || !color.rgb.every((n): n is number => typeof n === 'number' && Number.isFinite(n) && n >= 0 && n <= 255))
throw new TypeError('Compact material component differs.');
@@ -56,6 +59,9 @@ export function readCompactCompiler(value: unknown) {
const minimumFeatureScaleArcsec = value.minimumFeatureScaleArcsec;
if (minimumFeatureScaleArcsec !== undefined && (typeof minimumFeatureScaleArcsec !== 'number' || !Number.isFinite(minimumFeatureScaleArcsec) || minimumFeatureScaleArcsec <= 0))
throw new TypeError('Invalid compact feature scale.');
+ // A retained field is a pinned source artifact, including the envelope. Deleting it cannot select a finite-only fallback.
+ if (createHash('sha256').update(JSON.stringify(value.field)).digest('hex') !== value.provenance.modelSha256)
+ throw new TypeError('Compact retained model hash differs.');
return { objectId: value.objectId, field, scene, materials, sources, expected, minimumFeatureScaleArcsec };
}
@@ -66,12 +72,15 @@ export interface CompactCompilerBackend extends CompilerBakeBackend {
export async function replayCompactCompiler(root: string, pin: Pin, outputDirectory: string, backend: CompactCompilerBackend,
progress?: (progress: CompilerBakeProgress) => void) {
const input = readCompactCompiler(JSON.parse(gunzipSync(await pinned(root, pin), { maxOutputLength: 16 * 1024 * 1024 }).toString()));
- const field = createEmissionField(input.field), old = input.scene, origin = old.coordinates.localOriginArcsec;
+ const field = createPhotometricEmission(input.field), old = input.scene, origin = old.coordinates.localOriginArcsec;
+ const preparedPhysical = old.frame.referenceFrame === 'lab-sky-west-north-toward';
const scene = await bakeCompiler({ root, outputDirectory, id: old.volumeId ?? old.id, fieldIdentity: old.fieldIdentity,
+ sampling: old.sampling, preparedPhysical, historicalReplay: old.sampling.renderBudget === undefined,
boundsArcsec: old.boundsArcsec, skyBoundsArcsec: old.skyBoundsArcsec, minimumFeatureScaleArcsec: input.minimumFeatureScaleArcsec,
sampleEmission: field.sampleEmission, lenses: input.materials.map((material, index) => ({ id: material.sourceId,
- label: input.sources[index]!.label, sampleMaterial: field.createMaterialSampler(material.components) })),
- stars: old.stars.map(star => ({ ...star, positionArcsec: [star.positionUnits[0] + origin[0], star.positionUnits[1] + origin[1], star.positionUnits[2] + origin[2]] })), progress }, backend);
+ label: input.sources[index]!.label, sampleMaterial: field.createMaterialSampler(material.components, material.envelopeColors) })),
+ stars: old.stars.map(star => ({ ...star, positionArcsec: [star.positionUnits[0] + origin[0], star.positionUnits[1] + origin[1],
+ (preparedPhysical ? -star.positionUnits[2] : star.positionUnits[2]) + origin[2]] })), progress }, backend);
assert.equal(scene.alphaSha256, old.alphaSha256, 'Compact replay changed neutral opacity.');
assert.deepEqual(scene.sampling, old.sampling, 'Compact replay changed sampling.');
assert.equal(scene.starSprites?.atlas.sha256, old.starSprites?.atlas.sha256, 'Compact replay changed stellar sprites.');
diff --git a/labs/nebula/packages/volume-bake/src/compact-inputs/sampled.ts b/labs/nebula/packages/volume-bake/src/compact-inputs/sampled.ts
index 1cbb6c73f1..6ac58c0bfc 100644
--- a/labs/nebula/packages/volume-bake/src/compact-inputs/sampled.ts
+++ b/labs/nebula/packages/volume-bake/src/compact-inputs/sampled.ts
@@ -63,14 +63,30 @@ function colors(v: unknown): SampledColor[] {
return { rgb, covered: r.covered };
});
}
-export async function replayCompactSampled(
+type EmissionSampler = BakeCompilerOptions['sampleEmission'];
+/** Planning-only envelope: a feature unique to any lens remains represented without summing its brightness into the baked field. */
+export function maximumPlanningEmission(samplers: readonly EmissionSampler[]): EmissionSampler {
+ if (!samplers.length || samplers.some(sample => typeof sample !== 'function')) throw new TypeError('Planning requires at least one emission sampler.');
+ const inputs = [...samplers], value: [number, number, number] = [0, 0, 0];
+ return (x, y, z, out) => {
+ let maximum = 0;
+ for (const sample of inputs) {
+ value.fill(NaN); sample(x, y, z, value);
+ if (value.some(n => !Number.isFinite(n) || n < 0)) throw new TypeError('Planning components must write finite nonnegative emission.');
+ maximum = Math.max(maximum, ...value);
+ }
+ out.fill(maximum);
+ };
+}
+
+/** Verified scientific inputs for replay or a separately identified inspection bake. No fitting, source image access or output writes. */
+export async function prepareCompactSampledInputs(
root: string,
inputPin: CompilerPin,
- outputDirectory: string,
- backend: SampledReplayBackend,
+ backend: Pick,
+ signal: AbortSignal = new AbortController().signal,
) {
- const bakeCompiler = (options: BakeCompilerOptions) => bake(options, backend);
- const prepareCompilerStarSprites = backend.prepareStarSprites;
+ signal.throwIfAborted();
const m = object(
JSON.parse(
gunzipSync(await pinned(root, inputPin), {
@@ -82,8 +98,8 @@ export async function replayCompactSampled(
throw new Error("Invalid compact sampled model");
const recipe = readSampledRecipe(m.recipe),
original = readCompilerBakeResult(m.scene),
- id = text(m.sourceResult),
- signal = new AbortController().signal;
+ id = text(m.sourceResult);
+ if (id !== original.id) throw new TypeError('Compact source result differs from its retained scene.');
const fits = gunzipSync(await pinned(root, pin(m.particles)), {
maxOutputLength: 100_000_000,
}),
@@ -91,6 +107,15 @@ export async function replayCompactSampled(
if (geometrySha(fits) !== recipe.source.sha256) throw new Error("Compact particles differ from the scientific source pin");
const prepared = prepareSampledField(values, recipe, signal),
lensInputs = array(m.lenses).map(object);
+ const lensIds = lensInputs.map(lens => text(lens.id));
+ if (new Set(lensIds).size !== lensIds.length || lensIds.length !== original.lenses.length ||
+ original.lenses.some(lens => !lensIds.includes(lens.id))) throw new TypeError('Compact lenses differ from the retained scene.');
+ const expectedInput = object(m.expected), expected: Record = {};
+ for (const id of lensIds) {
+ const digest = expectedInput[id];
+ if (typeof digest !== 'string' || !/^[a-f0-9]{64}$/.test(digest)) throw new TypeError('Missing accepted compact sampled volume hash.');
+ expected[id] = digest;
+ }
const load = async (l: Record) => {
const sourceId = text(l.id),
weights = recipe.lensComponents[sourceId];
@@ -129,7 +154,10 @@ export async function replayCompactSampled(
fit && diffuse
? { atoms, coefficients, ejectaGain: finite(fit.ejectaGain), diffuse }
: undefined;
- const painter = prepareSampledMaterial(
+ const retained = { pointColors, windColors: colors(material.windColors), atomColors: colors(material.diffuseColors) };
+ if (retained.windColors.length !== recipe.terms.length || retained.atomColors.length !== atoms.length)
+ throw new TypeError('Retained material colors differ from sampled components.');
+ const prepareMaterial = () => prepareSampledMaterial(
values,
recipe,
prepared,
@@ -142,11 +170,7 @@ export async function replayCompactSampled(
weights,
fitData,
signal,
- {
- pointColors,
- windColors: colors(material.windColors),
- atomColors: colors(material.diffuseColors),
- },
+ retained,
);
return {
sourceId,
@@ -158,7 +182,7 @@ export async function replayCompactSampled(
diffuse,
)
: prepared.field(weights),
- painter,
+ prepareMaterial,
};
};
const referenceId = original.lenses[0]!.id,
@@ -181,10 +205,26 @@ export async function replayCompactSampled(
),
)
: prepared.field({ ejecta: 1, pwn: 1 });
+ const lenses = [];
+ for (const input of lensInputs) lenses.push(input === reference ? ref : await load(input));
+ const sources = lensInputs.map(l => ({ id: text(l.id), label: text(l.label), credit: text(l.credit), page: text(l.page) }));
+ return { id, original, recipe, neutralField, lenses, sources, expected,
+ samplePlanningEmission: maximumPlanningEmission([neutralField.sampleEmission, ...lenses.map(lens => lens.field.sampleEmission)]) };
+}
+
+export async function replayCompactSampled(root: string, inputPin: CompilerPin, outputDirectory: string, backend: SampledReplayBackend) {
+ const bakeCompiler = (options: BakeCompilerOptions) => bake(options, backend);
+ const prepareCompilerStarSprites = backend.prepareStarSprites, signal = new AbortController().signal;
+ const input = await prepareCompactSampledInputs(root, inputPin, backend, signal);
+ const { id, original, neutralField, expected, sources } = input;
const base = {
root,
id,
fieldIdentity: original.fieldIdentity,
+ sampling: original.sampling,
+ historicalReplay: original.sampling.renderBudget === undefined,
+ reservedStars: original.stars.length,
+ preparedPhysical: original.frame.referenceFrame === 'lab-sky-west-north-toward',
boundsArcsec: original.boundsArcsec,
skyBoundsArcsec: original.skyBoundsArcsec,
signal,
@@ -205,8 +245,9 @@ export async function replayCompactSampled(
],
});
const lenses = [];
- for (const input of lensInputs) {
- const l = input === reference ? ref : await load(input);
+ for (const l of input.lenses) {
+ signal.throwIfAborted();
+ const painter = l.prepareMaterial();
const bank = await bakeCompiler({
...base,
outputDirectory: `${outputDirectory}/${l.sourceId}`,
@@ -215,7 +256,7 @@ export async function replayCompactSampled(
{
id: l.sourceId,
label: l.label,
- sampleMaterial: l.painter.sampleMaterial,
+ sampleMaterial: painter.sampleMaterial,
},
],
});
@@ -235,7 +276,6 @@ export async function replayCompactSampled(
pin => pinned(root, pin),
backend,
);
- const expected = object(m.expected);
for (const lens of registered.lenses) {
const volume = object(
JSON.parse((await pinned(root, lens.volume)).toString()),
@@ -259,11 +299,6 @@ export async function replayCompactSampled(
return {
id,
scene,
- sources: lensInputs.map((l) => ({
- id: text(l.id),
- label: text(l.label),
- credit: text(l.credit),
- page: text(l.page),
- })),
+ sources,
};
}
diff --git a/labs/nebula/packages/volume-bake/src/compiler/bake.ts b/labs/nebula/packages/volume-bake/src/compiler/bake.ts
index 0d7606500a..3117ba46b7 100644
--- a/labs/nebula/packages/volume-bake/src/compiler/bake.ts
+++ b/labs/nebula/packages/volume-bake/src/compiler/bake.ts
@@ -10,10 +10,14 @@ import type { VolumeSlices } from '@cssearth/volume-core/contracts/volume-slices
import { recolorCloudSlices } from '../slices/material.ts';
import { bakeMasterVolumeSlices } from '../slices/emission.ts';
import { compilerSlabMaterial } from '@cssearth/volume-core/materials/slab-material';
+import { optimizeVolumeLayers, readLayerOptimizationReport } from '@cssearth/volume-core/sampling/layer-optimization';
+import { readVolumeLayerPlan } from '@cssearth/volume-core/contracts/volume-slices';
+import { createRenderElementBudget, maximumRenderSlabs, readRenderElementBudget, readRenderElementProfile, renderElementCount,
+ type RenderElementProfile } from '@cssearth/volume-core/contracts/render-element-budget';
import { readCompilerBakeResult, validCompilerStarSize, validCompilerStarMaterials, type CompilerBakeResult, type CompilerPin, type PreparedCompilerStar, type CompilerStarMaterial, type CompilerStarSprites } from '@cssearth/volume-core/contracts/compiler-bake';
import type { EmissionBounds, EmissionVector3, SkyBounds } from '@cssearth/volume-core/contracts/emission';
-import { compilerFrame, compilerSliceCounts, validCompilerBounds } from '@cssearth/volume-core/coordinates/compiler-frame';
+import { compilerFrame, compilerPreparedPoint, compilerPreparedSlices, compilerSliceCounts, validCompilerBounds } from '@cssearth/volume-core/coordinates/compiler-frame';
export { compilerFrame, compilerSliceCounts } from '@cssearth/volume-core/coordinates/compiler-frame';
export type { CompilerBakeResult, CompilerLensVolume, CompilerPin, PreparedCompilerStar } from '@cssearth/volume-core/contracts/compiler-bake';
@@ -31,6 +35,8 @@ export interface CompiledVolumeArtifact {
resources: readonly { path: string; sha256: string; bytes: number }[];
}
export interface CompilerBakeBackend {
+ /** Host-owned, tested cost of its retained geometry, points and delivery wrappers. */
+ renderBudget?: RenderElementProfile;
compileVolume(input: { id: string; frame: DensityVolumeFrame; slices: VolumeSlices }): CompiledVolumeArtifact;
prepareStarSprites(root: string, outputDirectory: string, stars: readonly PreparedCompilerStar[]): Promise<{ starSprites?: CompilerStarSprites }>;
}
@@ -39,6 +45,16 @@ export interface BakeCompilerOptions {
root: string; outputDirectory: string; id: string; fieldIdentity: string;
boundsArcsec: EmissionBounds; skyBoundsArcsec: SkyBounds;
sampleEmission(xWestArcsec: number, yNorthArcsec: number, zAwayArcsec: number, outRgb: Vector3): void;
+ /** Optional common support envelope for several component mixtures. Used only for offline allocation. */
+ samplePlanningEmission?: BakeCompilerOptions['sampleEmission'];
+ /** Exact saved sampling for compact replay or a shared component layout. Absence selects the automatic budget. */
+ sampling?: CompilerBakeResult['sampling'];
+ /** Internal verified compact replay only. Never a research recipe option. New budget receipts still validate. */
+ historicalReplay?: boolean;
+ /** Component unions may add their pinned stars only after individual material bakes. Never below stars.length. */
+ reservedStars?: number;
+ /** Historical compact receipts retain their original unreflected coordinate convention. */
+ preparedPhysical?: boolean;
/** Smallest supported kernel scale; reduces slab spacing for thin, tilted structures. */
minimumFeatureScaleArcsec?: number;
lenses: CompilerLensInput[]; stars?: CompilerStarInput[]; signal?: AbortSignal;
@@ -48,6 +64,53 @@ export interface BakeCompilerOptions {
const IMAGE_WIDTH = 512 as const, DEPTH_SAMPLES = 4 as const;
const json = (value: unknown) => Buffer.from(JSON.stringify(value) + '\n');
function cancel(signal?: AbortSignal) { if (signal?.aborted) throw new DOMException('Nebula compile cancelled.', 'AbortError'); }
+
+/** New authoring plans once; immutable compact replay uses its saved quadrature and partition. */
+export function compilerSampling(options: Pick &
+ { renderProfile?: RenderElementProfile; starCount?: number }): CompilerBakeResult['sampling'] {
+ const starCount = options.starCount ?? 0, profile = options.renderProfile && readRenderElementProfile(options.renderProfile);
+ if (!Number.isSafeInteger(starCount) || starCount < 0 ||
+ (options.historicalReplay !== undefined && typeof options.historicalReplay !== 'boolean')) throw new TypeError('Invalid compiler star reservation or replay mode.');
+ if (options.historicalReplay && !options.sampling) throw new TypeError('Historical replay requires exact saved sampling.');
+ if (options.sampling !== undefined) {
+ const saved = options.sampling, counts = saved.sliceCounts;
+ if (!counts || saved.imageWidth !== IMAGE_WIDTH || saved.samplesPerSlab !== DEPTH_SAMPLES ||
+ [counts.x, counts.y, counts.z].some(n => !Number.isInteger(n) || n < 1 || n > 512)) throw new TypeError('Invalid saved compiler sampling.');
+ if (saved.layerPlan !== undefined) {
+ const plan = readVolumeLayerPlan(saved.layerPlan);
+ if (plan.referenceSamplesPerSlab !== DEPTH_SAMPLES || (['x', 'y', 'z'] as const).some(axis => plan.axes[axis].length !== counts[axis]))
+ throw new TypeError('Saved compiler sampling differs from its layer plan.');
+ if (saved.layerOptimization !== undefined) readLayerOptimizationReport(saved.layerOptimization, plan);
+ } else if (saved.layerOptimization !== undefined) throw new TypeError('Saved layer optimization requires a layer plan.');
+ const slabCount = counts.x + counts.y + counts.z;
+ if (saved.renderBudget !== undefined) {
+ if (!profile) throw new TypeError('A budgeted compiler scene requires its host render profile.');
+ readRenderElementBudget(saved.renderBudget, starCount, slabCount, profile);
+ return structuredClone(saved);
+ }
+ if (options.historicalReplay) {
+ if (profile) {
+ const elements = renderElementCount(profile, starCount, slabCount);
+ options.progress?.({ phase: 'volume', completed: 0, total: slabCount,
+ message: `Replaying pinned historical sampling unchanged: ${elements} reserved renderer elements (${elements > profile.maximumElements ? 'over budget' : 'within budget'}).` });
+ }
+ return structuredClone(saved);
+ }
+ if (!profile) throw new TypeError('New compiler sampling requires a host render-element profile.');
+ return { ...structuredClone(saved), renderBudget: createRenderElementBudget(profile, starCount, slabCount) };
+ }
+ if (!profile) throw new TypeError('New compiler sampling requires a host render-element profile.');
+ const maximumLayers = Math.min(500, maximumRenderSlabs(profile, starCount));
+ const { plan, report } = optimizeVolumeLayers({ bounds: options.boundsArcsec,
+ maximumLayers,
+ referenceSliceCounts: compilerSliceCounts(options.boundsArcsec, options.minimumFeatureScaleArcsec), referenceSamplesPerSlab: DEPTH_SAMPLES,
+ sampleEmission: options.samplePlanningEmission ?? options.sampleEmission, signal: options.signal,
+ onProgress: ({ completed, total }) => options.progress?.({ phase: 'volume', completed, total,
+ message: `Choosing at most ${maximumLayers} XYZ slabs within ${profile.maximumElements} retained renderer elements` }) });
+ return { sliceCounts: { x: plan.axes.x.length, y: plan.axes.y.length, z: plan.axes.z.length }, imageWidth: IMAGE_WIDTH,
+ samplesPerSlab: DEPTH_SAMPLES, layerPlan: plan, layerOptimization: report,
+ renderBudget: createRenderElementBudget(profile, starCount, plan.axes.x.length + plan.axes.y.length + plan.axes.z.length) };
+}
function validSkyBounds(bounds: SkyBounds): boolean {
return Array.isArray(bounds?.min) && Array.isArray(bounds?.max) && bounds.min.length === 2 && bounds.max.length === 2 &&
bounds.min.every((n, i) => Number.isFinite(n) && Number.isFinite(bounds.max[i]) && n < bounds.max[i]!);
@@ -87,33 +150,44 @@ export async function bakeCompiler(options: BakeCompilerOptions, backend: Compil
const lensIds = new Set();
for (const lens of options.lenses) if (!/^[a-z0-9][a-z0-9-]{0,95}$/.test(lens.id) || lensIds.has(lens.id) || !lens.label.trim() || typeof lens.sampleMaterial !== 'function')
throw new TypeError('Compiler lenses require unique safe identities and a 3D material sampler.'); else lensIds.add(lens.id);
- const { origin, localBounds, frame } = compilerFrame(boundsArcsec);
+ const preparedPhysical = options.preparedPhysical ?? true;
+ if (typeof preparedPhysical !== 'boolean') throw new TypeError('Invalid compiler frame convention.');
+ const { origin, localBounds, frame } = compilerFrame(boundsArcsec, preparedPhysical);
const starIds = new Set(), stars: PreparedCompilerStar[] = [];
for (const star of options.stars ?? []) {
if (!star.id || star.id.length > 128 || starIds.has(star.id) || !Array.isArray(star.positionArcsec) || star.positionArcsec.length !== 3 || !star.positionArcsec.every(Number.isFinite) ||
!Array.isArray(star.rgb) || star.rgb.length !== 3 || !star.rgb.every(n => Number.isInteger(n) && n >= 0 && n <= 255) ||
!validCompilerStarSize(star) || !Number.isFinite(star.alpha) || star.alpha < 0 || star.alpha > 1 || !validCompilerStarMaterials(star.materials, lensIds))
throw new TypeError('Invalid compiler star input.');
- starIds.add(star.id); stars.push({ id: star.id, positionUnits: star.positionArcsec.map((n, i) => n - origin[i]!) as EmissionVector3,
+ const localPosition = star.positionArcsec.map((n, i) => n - origin[i]!) as EmissionVector3;
+ starIds.add(star.id); stars.push({ id: star.id, positionUnits: preparedPhysical ? compilerPreparedPoint(localPosition) : localPosition,
rgb: [...star.rgb], ...(star.diameterUnits !== undefined ? { diameterUnits: star.diameterUnits } : { widthPx: star.widthPx }), alpha: star.alpha,
...(star.materials ? { materials: structuredClone(star.materials) } : {}) });
}
if (stars.length > 5000) throw new TypeError('Compiler star count exceeds the retained point budget.');
- const sliceCounts = compilerSliceCounts(boundsArcsec, options.minimumFeatureScaleArcsec);
+ const reservedStars = options.reservedStars ?? options.sampling?.renderBudget?.starCount ?? stars.length;
+ if (!Number.isSafeInteger(reservedStars) || reservedStars < stars.length) throw new TypeError('Compiler star reservation cannot omit retained stars.');
+ const sampling = compilerSampling({ ...options, renderProfile: backend.renderBudget, starCount: reservedStars }), { sliceCounts, layerPlan } = sampling;
const output = containedPath(root, outputDirectory), masterDirectory = containedPath(output, 'masters');
const neutralDirectory = containedPath(output, 'neutral');
await mkdir(output, { recursive: true }); cancel(signal);
const provenance = { schema: 'cssearth-compiler-volume-provenance@1', fieldIdentity: options.fieldIdentity,
coordinates: { axes: ['west', 'north', 'away'], units: 'arcsec', localOriginArcsec: origin,
- mapping: 'absoluteArcsec = localUnits + localOriginArcsec', earthView: 'observer-at-negative-z-looking-away' },
+ ...(preparedPhysical ? { mapping: 'sourceArcsec = [preparedWest, preparedNorth, -preparedToward] + localOriginArcsec',
+ preparedAxes: ['west', 'north', 'toward'], earthView: 'source-observer-at-negative-z; prepared-observer-at-positive-z' }
+ : { mapping: 'absoluteArcsec = localUnits + localOriginArcsec', earthView: 'observer-at-negative-z-looking-away' }) },
boundsArcsec, skyBoundsArcsec, minimumFeatureScaleArcsec: options.minimumFeatureScaleArcsec,
interpretation: 'Neutral relative display emission from the supplied fitted field. Source RGB supplies material chromaticity only and cannot change support, opacity, or depth.',
limitations: ['This prepared preview transports the caller-owned analytic field; it does not define or validate the scientific model.',
- 'Angular depth is an inferred display coordinate, not a measured line-of-sight distance.', 'Finite slabs, four depth samples per slab, and RGBA8 opacity approximate the continuous field.'] };
+ 'Angular depth is an inferred display coordinate, not a measured line-of-sight distance.',
+ layerPlan ? 'Grouped finite slabs retain four depth samples per reference cell; RGBA8 opacity and plane collapse approximate the continuous field. The coarse optimization estimate is not visual acceptance.'
+ : 'Finite slabs, four depth samples per slab, and RGBA8 opacity approximate the continuous field.'],
+ ...(sampling.layerOptimization ? { layerOptimization: sampling.layerOptimization } : {}),
+ ...(sampling.renderBudget ? { renderBudget: sampling.renderBudget } : {}) };
const totalSlices = sliceCounts.x + sliceCounts.y + sliceCounts.z;
options.progress?.({ phase: 'volume', completed: 0, total: totalSlices * 2, message: 'Preparing shared neutral geometry' });
let calls = 0;
- const baked = await bakeMasterVolumeSlices({ boundsKpc: localBounds, sliceCounts, samplesPerSlab: DEPTH_SAMPLES,
+ const baked = await bakeMasterVolumeSlices({ boundsKpc: localBounds, sliceCounts, samplesPerSlab: DEPTH_SAMPLES, ...(layerPlan ? { layerPlan } : {}),
exposureGain: 1, masterWidth: IMAGE_WIDTH, masterDirectory,
deliveryBanks: [{ width: IMAGE_WIDTH, outputDirectory: neutralDirectory, imageEncoding: { format: 'png' } }], unitsPerSourceUnit: 1,
provenance, cropTransparent: true, allowEmpty: false, sampleEmission(x, y, z, out) {
@@ -154,7 +228,9 @@ export async function bakeCompiler(options: BakeCompilerOptions, backend: Compil
for (let index = 0; index < banks.length; index++) {
cancel(signal); const bank = banks[index]!;
options.progress?.({ phase: 'compile', completed: index, total: banks.length, message: 'Compiling retained volume materials' });
- const volume = backend.compileVolume({ id: `compiler-${options.id}`, frame, slices: bank.slices });
+ // Keep the source sampling and pinned PNG bytes untouched. Cross the handedness boundary once,
+ // before the physical renderer sees coordinates, by reflecting prepared vertices and points.
+ const volume = backend.compileVolume({ id: `compiler-${options.id}`, frame, slices: preparedPhysical ? compilerPreparedSlices(bank.slices) : bank.slices });
pins.set(bank.id, await pin(root, relative(root, containedPath(bank.directory, 'volume.json')), volume));
}
options.progress?.({ phase: 'compile', completed: banks.length, total: banks.length, message: 'Prepared final cloud and materials' });
@@ -165,5 +241,5 @@ export async function bakeCompiler(options: BakeCompilerOptions, backend: Compil
sourceImage: { width: 512, height: 512 }, coordinates: { axes: ['west', 'north', 'away'], localOriginArcsec: origin,
earthView: 'observer-at-negative-z-looking-away' }, neutral: pins.get('neutral'), alphaSha256: neutralAlpha,
lenses: painted.map(item => ({ id: item.input.id, label: item.input.label, volume: pins.get(item.input.id), coverage: item.coverage })),
- stars, ...starSprites, sampling: { sliceCounts, imageWidth: IMAGE_WIDTH, samplesPerSlab: DEPTH_SAMPLES } });
+ stars, ...starSprites, sampling });
}
diff --git a/labs/nebula/packages/volume-bake/src/compiler/compiler-sampling.test.ts b/labs/nebula/packages/volume-bake/src/compiler/compiler-sampling.test.ts
new file mode 100644
index 0000000000..56d0ad54de
--- /dev/null
+++ b/labs/nebula/packages/volume-bake/src/compiler/compiler-sampling.test.ts
@@ -0,0 +1,103 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { mkdtemp, readFile, rm } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+import { gunzipSync } from 'node:zlib';
+import { bakeCompiler, compilerSampling, type CompilerBakeResult } from './bake.ts';
+import { readCompactCompiler } from '../compact-inputs/compiler.ts';
+import { validateVolumeLayerSlices, type VolumeSlices } from '@cssearth/volume-core/contracts/volume-slices';
+import { createRenderElementBudget, type RenderElementProfile } from '@cssearth/volume-core/contracts/render-element-budget';
+
+const boundsArcsec = { min: [-1, -1, -1] as [number, number, number], max: [1, 1, 1] as [number, number, number] };
+const renderProfile: RenderElementProfile = { schema: 'cssearth-render-element-profile@1', id: 'test-retained-renderer@1',
+ maximumElements: 500, elementsPerSlab: 3, elementsPerStar: 1, reservedElements: 47 };
+
+test('the actual compiler default creates a bounded plan without an opt-in sampling override', () => {
+ let calls = 0;
+ const sampling = compilerSampling({ boundsArcsec, renderProfile, sampleEmission(_x, _y, _z, out) { calls++; out[0] = out[1] = out[2] = .2; } });
+ assert.ok(sampling.layerPlan, 'The default authoring path must use the optimizer.');
+ assert.ok(sampling.layerOptimization);
+ assert.equal(sampling.layerOptimization.maximumLayers, 151);
+ assert.ok(sampling.sliceCounts.x + sampling.sliceCounts.y + sampling.sliceCounts.z <= 151);
+ assert.ok(sampling.renderBudget && sampling.renderBudget.totalElements <= 500);
+ assert.equal(calls, 192 * 3 * 64 * 64 * 4 * 4, 'The real default probes every reference cell at four depth samples and four in-plane phases.');
+ assert.deepEqual(sampling.layerPlan.referenceSliceCounts, { x: 192, y: 192, z: 192 });
+});
+
+test('actual accepted M42 sampling bypasses optimization with its historical omission unchanged', async () => {
+ const compact = readCompactCompiler(JSON.parse(gunzipSync(await readFile('src/objects/m42/source/bake-inputs.json.gz')).toString()));
+ const saved = compact.scene.sampling, before = JSON.stringify(saved);
+ assert.equal(saved.layerPlan, undefined);
+ const forbidden = () => { throw new Error('Historical replay must not probe or replan the source field.'); };
+ const messages: string[] = [];
+ const actual = compilerSampling({ boundsArcsec: compact.scene.boundsArcsec, sampling: saved, historicalReplay: true,
+ renderProfile, starCount: compact.scene.stars.length, progress: value => messages.push(value.message),
+ sampleEmission: forbidden, samplePlanningEmission: forbidden });
+ assert.ok(messages.some(message => message.includes('over budget')));
+ assert.equal(JSON.stringify(actual), before);
+ assert.equal('layerPlan' in actual, false);
+ assert.notEqual(actual, saved);
+ actual.sliceCounts.x++;
+ assert.equal(JSON.stringify(saved), before, 'Replay must not mutate the accepted receipt.');
+});
+
+test('saved planned sampling is replayed exactly and rejects a mismatching retained count', () => {
+ const sampling: CompilerBakeResult['sampling'] = { imageWidth: 512, samplesPerSlab: 4, sliceCounts: { x: 1, y: 1, z: 2 },
+ layerPlan: { schema: 'cssearth-volume-layer-plan@1', referenceSliceCounts: { x: 4, y: 4, z: 4 }, referenceSamplesPerSlab: 4,
+ axes: { x: [{ startCell: 0, endCell: 4 }], y: [{ startCell: 0, endCell: 4 }],
+ z: [{ startCell: 0, endCell: 3 }, { startCell: 3, endCell: 4 }] } } };
+ const sampleEmission = () => { throw new Error('Saved plans must not be recomputed.'); };
+ assert.deepEqual(compilerSampling({ boundsArcsec, sampling, sampleEmission, renderProfile }),
+ { ...sampling, renderBudget: createRenderElementBudget(renderProfile, 0, 4) });
+ assert.throws(() => compilerSampling({ boundsArcsec, sampling: { ...sampling, sliceCounts: { x: 1, y: 1, z: 3 } }, sampleEmission, renderProfile }), /differs from/);
+});
+
+test('new authoring reserves stars before probing and cannot use explicit old sampling as a budget bypass', () => {
+ const forbidden = () => { throw new Error('Invalid admission must fail before field sampling.'); };
+ assert.throws(() => compilerSampling({ boundsArcsec, renderProfile, starCount: 445, sampleEmission: forbidden }), /no complete XYZ/);
+ assert.throws(() => compilerSampling({ boundsArcsec, sampleEmission: forbidden }), /requires a host/);
+ const sampling = { imageWidth: 512 as const, samplesPerSlab: 4 as const, sliceCounts: { x: 50, y: 50, z: 52 } };
+ assert.throws(() => compilerSampling({ boundsArcsec, sampling, renderProfile, sampleEmission: forbidden }), /503 retained elements/);
+ const valid = { ...sampling, sliceCounts: { x: 50, y: 50, z: 51 }, renderBudget: createRenderElementBudget(renderProfile, 0, 151) };
+ assert.throws(() => compilerSampling({ boundsArcsec, sampling: valid, renderProfile, starCount: 1,
+ historicalReplay: true, sampleEmission: forbidden }), /star reservation/, 'Historical mode cannot bypass a new budget receipt.');
+ assert.throws(() => compilerSampling({ boundsArcsec, renderProfile, historicalReplay: true, sampleEmission: forbidden }), /requires exact saved/);
+});
+
+test('the optimizer receives the remaining budget after retained stars, not a declaration-only 500 limit', () => {
+ const sampling = compilerSampling({ boundsArcsec, renderProfile, starCount: 3,
+ sampleEmission(_x, _y, _z, out) { out[0] = out[1] = out[2] = .2; } });
+ assert.equal(sampling.layerOptimization?.maximumLayers, 150);
+ assert.equal(sampling.renderBudget?.starCount, 3);
+ assert.ok(sampling.renderBudget && sampling.renderBudget.totalElements <= 500);
+});
+
+test('a small historical compiler replay keeps unreflected source geometry, sampling and physical intervals absent', async t => {
+ const root = await mkdtemp(join(tmpdir(), 'legacy-compiler-sampling-'));
+ t.after(() => rm(root, { recursive: true, force: true }));
+ const compiled: VolumeSlices[] = [];
+ const sampling = { sliceCounts: { x: 1, y: 1, z: 2 }, imageWidth: 512 as const, samplesPerSlab: 4 as const };
+ const scene = await bakeCompiler({ root, outputDirectory: 'bake', id: 'historical', fieldIdentity: 'a'.repeat(64),
+ boundsArcsec: { min: [10, 20, 30], max: [12, 22, 32] }, skyBoundsArcsec: { min: [10, 20], max: [12, 22] },
+ sampling, historicalReplay: true, preparedPhysical: false, samplePlanningEmission() { throw new Error('Saved replay must skip the optimizer.'); },
+ sampleEmission(_x, _y, _z, out) { out[0] = out[1] = out[2] = .2; },
+ lenses: [{ id: 'color', label: 'Color', sampleMaterial(_x, _y, z, out) {
+ out[0] = z < 31 ? 255 : 0; out[1] = 0; out[2] = z < 31 ? 0 : 255; return true;
+ } }], progress() {},
+ }, { compileVolume({ frame, slices }) {
+ assert.equal(frame.referenceFrame, 'lab-sky-angular');
+ compiled.push(structuredClone(slices));
+ return { resources: slices.quads.map(q => ({ path: q.texturePath, sha256: q.sha256, bytes: q.bytes })) };
+ }, async prepareStarSprites() { return {}; } });
+ assert.deepEqual(scene.sampling, sampling);
+ assert.equal(scene.frame.referenceFrame, 'lab-sky-angular');
+ assert.equal(compiled.length, 2);
+ const neutral: VolumeSlices = JSON.parse(await readFile(join(root, 'bake/neutral/volume-slices.json'), 'utf8'));
+ assert.equal(JSON.stringify(compiled[0]), JSON.stringify(neutral), 'Legacy serialized compile input is the saved source manifest with no frame or metadata conversion.');
+ assert.equal(validateVolumeLayerSlices(neutral), undefined);
+ assert.equal('layerPlan' in neutral.approximation, false);
+ assert.ok(neutral.quads.every(q => !('slab' in q)));
+ assert.deepEqual(neutral.quads.filter(q => q.axis === 'z').map(q => q.center[2]), [-.5, .5]);
+ for (const bank of compiled) assert.deepEqual(bank.quads.filter(q => q.axis === 'z').map(q => q.center[2]), [-.5, .5]);
+});
diff --git a/labs/nebula/packages/volume-bake/src/compiler/component-layout.ts b/labs/nebula/packages/volume-bake/src/compiler/component-layout.ts
index 523f8562a5..870a3b8612 100644
--- a/labs/nebula/packages/volume-bake/src/compiler/component-layout.ts
+++ b/labs/nebula/packages/volume-bake/src/compiler/component-layout.ts
@@ -5,11 +5,12 @@ import { createHash } from 'node:crypto';
import sharp from 'sharp';
import type { CompilerBakeResult, CompilerLensVolume, CompilerPin } from '@cssearth/volume-core/contracts/compiler-bake';
import type { CompilerBakeBackend, CompiledVolumeArtifact } from './bake.ts';
+import { COMPILER_PHYSICAL_REFERENCE, compilerPreparedSlices } from '@cssearth/volume-core/coordinates/compiler-frame';
import { hash as geometrySha } from '../compact-inputs/io.ts';
const jointRecord = (v: unknown): v is Record => v !== null && typeof v === 'object' && !Array.isArray(v);
-import type { VolumeSlices, VolumeSliceQuad } from '@cssearth/volume-core/contracts/volume-slices';
+import { readVolumeLayerPlan, readVolumeSlabInterval, validateVolumeLayerSlices, type VolumeSlabInterval, type VolumeSlices, type VolumeSliceQuad } from '@cssearth/volume-core/contracts/volume-slices';
export interface ComponentVolumeArtifact extends CompiledVolumeArtifact {
stacks: readonly { leaves: readonly { id: string }[] }[];
@@ -21,26 +22,34 @@ export interface ComponentBankBackend {
}
type Vec = [number, number, number];
+const vector = (v: unknown): v is Vec => Array.isArray(v) && v.length === 3 && v.every(n => typeof n === 'number' && Number.isFinite(n));
+const uv = (v: unknown): v is [number, number] => Array.isArray(v) && v.length === 2 && v.every(n => typeof n === 'number' && Number.isFinite(n));
export interface RegisteredSlice {
id: string; widthPx: number; heightPx: number; vertices: [Vec, Vec, Vec, Vec];
texturePath: string; sha256: string; bytes: number;
+ slab?: VolumeSlabInterval;
}
-function readSlices(value: unknown): RegisteredSlice[] {
+function readSlices(value: unknown): VolumeSliceQuad[] {
if (!jointRecord(value) || !Array.isArray(value.quads) || !value.quads.length || value.quads.length > 1536)
throw new TypeError('Invalid component slice catalogue.');
const ids = new Set();
- return value.quads.map((q: unknown): RegisteredSlice => {
+ return value.quads.map((q: unknown): VolumeSliceQuad => {
if (!jointRecord(q) || typeof q.id !== 'string' || !/^[xyz]-\d+$/.test(q.id) || ids.has(q.id) ||
![q.widthPx, q.heightPx, q.bytes].every(n => Number.isSafeInteger(n) && Number(n) > 0) ||
Number(q.widthPx) > 1024 || Number(q.heightPx) > 2048 ||
typeof q.texturePath !== 'string' || !/^slices\/[xyz]\/\d+\.png$/.test(q.texturePath) ||
typeof q.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(q.sha256) || !Array.isArray(q.vertices) || q.vertices.length !== 4 ||
- !q.vertices.every(p => Array.isArray(p) && p.length === 3 && p.every(n => typeof n === 'number' && Number.isFinite(n))))
+ !q.vertices.every(vector) || !vector(q.center) || !vector(q.normal) || !Array.isArray(q.uvs) || q.uvs.length !== 4 || !q.uvs.every(uv) ||
+ (q.axis !== 'x' && q.axis !== 'y' && q.axis !== 'z') || q.axis !== q.id[0] || q.sliceIndex !== Number(q.id.slice(2)) ||
+ typeof q.alphaCoverage !== 'number' || !Number.isFinite(q.alphaCoverage) || q.alphaCoverage < 0 || q.alphaCoverage > 1)
throw new TypeError('Invalid registered component quad.');
ids.add(q.id);
const vertices = q.vertices.map(p => [Number(p[0]), Number(p[1]), Number(p[2])] as Vec);
return { id: q.id, widthPx: Number(q.widthPx), heightPx: Number(q.heightPx), bytes: Number(q.bytes),
- texturePath: q.texturePath, sha256: q.sha256, vertices: [vertices[0]!, vertices[1]!, vertices[2]!, vertices[3]!] };
+ texturePath: q.texturePath, sha256: q.sha256, vertices: [vertices[0]!, vertices[1]!, vertices[2]!, vertices[3]!],
+ axis: q.axis, sliceIndex: Number(q.sliceIndex), center: q.center, normal: q.normal,
+ uvs: [q.uvs[0]!, q.uvs[1]!, q.uvs[2]!, q.uvs[3]!], alphaCoverage: q.alphaCoverage,
+ ...(q.slab === undefined ? {} : { slab: readVolumeSlabInterval(q.slab) }) };
});
}
const dot = (a: Vec, b: Vec) => a.reduce((sum, n, i) => sum + n * b[i]!, 0);
@@ -53,6 +62,7 @@ export function mergeSliceLayout(reference: RegisteredSlice, components: Registe
let left = 0, top = 0, right = reference.widthPx, bottom = reference.heightPx;
for (const q of components) {
if (q.id !== reference.id) throw new TypeError('Cannot merge different physical slabs.');
+ if (JSON.stringify(q.slab) !== JSON.stringify(reference.slab)) throw new TypeError('Cannot merge different physical integration intervals.');
const delta = minus(q.vertices[0], reference.vertices[0]), cu = step(q, 1, q.widthPx), cv = step(q, 3, q.heightPx);
const x = dot(delta, u) / dot(u, u), y = dot(delta, v) / dot(v, v), ix = Math.round(x), iy = Math.round(y);
const tolerance = 1e-7 * Math.max(1, ...u.map(Math.abs), ...v.map(Math.abs));
@@ -95,7 +105,35 @@ export async function registerComponentBanks(root: string, outputDirectory: stri
lenses: CompilerLensVolume[], signal: AbortSignal,
readPinned: (pin: CompilerPin) => Promise, backend: ComponentBankBackend): Promise {
const readVolume = async (pin: CompilerPin) => backend.validateVolume(JSON.parse((await readPinned(pin)).toString()));
- const catalogue = async (pin: CompilerPin) => readSlices(JSON.parse(await readFile(resolve(root, dirname(pin.path), 'volume-slices.json'), 'utf8')));
+ const catalogue = async (pin: CompilerPin) => {
+ const value: unknown = JSON.parse(await readFile(resolve(root, dirname(pin.path), 'volume-slices.json'), 'utf8'));
+ const rawPlan = jointRecord(value) && jointRecord(value.approximation) ? value.approximation.layerPlan : undefined;
+ const plan = rawPlan === undefined ? undefined : readVolumeLayerPlan(rawPlan);
+ if (JSON.stringify(plan) !== JSON.stringify(neutral.sampling.layerPlan))
+ throw new TypeError('Component bank layer plan differs from its canonical sampling.');
+ const quads = readSlices(value);
+ if (quads.some(q => Boolean(q.slab) !== Boolean(plan))) throw new TypeError('Component bank omitted its physical slab intervals.');
+ if (plan) {
+ if (!jointRecord(value) || !jointRecord(value.boundsUnits) || !vector(value.boundsUnits.min) || !vector(value.boundsUnits.max) ||
+ JSON.stringify(value.boundsUnits) !== JSON.stringify(neutral.frame.boundsUnits) || !jointRecord(value.approximation) ||
+ value.approximation.samplesPerSlab !== neutral.sampling.samplesPerSlab || !jointRecord(value.approximation.sliceCounts))
+ throw new TypeError('Component catalogue bounds or sampling differ from its canonical plan.');
+ const counts = value.approximation.sliceCounts;
+ if ((['x', 'y', 'z'] as const).some(axis => counts[axis] !== neutral.sampling.sliceCounts[axis]))
+ throw new TypeError('Component catalogue counts differ from its canonical plan.');
+ validateVolumeLayerSlices({ quads, boundsUnits: { min: value.boundsUnits.min, max: value.boundsUnits.max }, provenance: {}, approximation: {
+ method: 'Registered catalogue validation.', radialEmission: 'none', limitations: [], samplesPerSlab: neutral.sampling.samplesPerSlab,
+ opticalWeight: 1, exposureGain: 1, sliceCounts: neutral.sampling.sliceCounts, slabPitchUnits: { x: 1, y: 1, z: 1 }, layerPlan: plan } });
+ }
+ return quads;
+ };
+ const pixels = async (volume: CompilerPin, source: RegisteredSlice) => {
+ const bytes = await readPinned({ path: `${dirname(volume.path)}/${source.texturePath}`, sha256: source.sha256 });
+ const decoded = await sharp(bytes).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
+ if (bytes.length !== source.bytes || decoded.info.width !== source.widthPx || decoded.info.height !== source.heightPx || decoded.info.channels !== 4)
+ throw new TypeError('Component raster differs from its registered crop.');
+ return decoded.data;
+ };
const banks = await Promise.all([{ id: 'neutral', volume: neutral.neutral }, ...lenses].map(async item => ({
...item, original: await readVolume(item.volume), slices: new Map((await catalogue(item.volume)).map(q => [q.id, q])),
resources: [] as Array<{ path: string; sha256: string; bytes: number; width: number; height: number }>, alpha: createHash('sha256'),
@@ -109,15 +147,8 @@ export async function registerComponentBanks(root: string, outputDirectory: stri
if (candidates.some(q => !q)) throw new TypeError('Prepared component mesh is missing its registered crop.');
const reference = mergeSliceLayout(candidates[0]!, candidates.slice(1)), unionPixels = Buffer.alloc(reference.widthPx * reference.heightPx * 4);
await Promise.all(banks.map(async bank => {
- const source = bank.slices.get(id); let pixels: Buffer | undefined;
- if (source) {
- const bytes = await readPinned({ path: `${dirname(bank.volume.path)}/${source.texturePath}`, sha256: source.sha256 });
- const decoded = await sharp(bytes).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
- if (bytes.length !== source.bytes || decoded.info.width !== source.widthPx || decoded.info.height !== source.heightPx || decoded.info.channels !== 4)
- throw new TypeError('Component raster differs from its registered crop.');
- pixels = decoded.data;
- }
- const padded = padComponentSlice(reference, source, pixels), opacity = Buffer.alloc(reference.widthPx * reference.heightPx);
+ const source = bank.slices.get(id), rgba = source ? await pixels(bank.volume, source) : undefined;
+ const padded = padComponentSlice(reference, source, rgba), opacity = Buffer.alloc(reference.widthPx * reference.heightPx);
for (let i = 0; i < opacity.length; i++) {
const a = padded[4 * i + 3]!; opacity[i] = a;
if (a > unionPixels[4 * i + 3]!) { unionPixels[4 * i] = unionPixels[4 * i + 1] = unionPixels[4 * i + 2] = 255; unionPixels[4 * i + 3] = a; }
@@ -136,12 +167,28 @@ export async function registerComponentBanks(root: string, outputDirectory: stri
uvs: [[0, 0], [1, 0], [1, 1], [0, 1]], sha256: geometrySha(bytes), bytes: bytes.length,
alphaCoverage: unionPixels.reduce((n, a, i) => n + Number(i % 4 === 3 && a > 0), 0) / (reference.widthPx * reference.heightPx) });
}
+ if (neutral.sampling.layerPlan) for (const reference of banks[0]!.slices.values()) if (!active.has(reference.id)) {
+ signal.throwIfAborted();
+ // The physical transport validates a complete partition. Reintroduce only
+ // verified zero-alpha source quads here; compileVolume owns their pruning
+ // from the render graph, so no empty output raster/resource is generated.
+ for (const bank of banks) {
+ const source = bank.slices.get(reference.id);
+ if (!source) throw new TypeError('Component catalogue omitted a planned slab.');
+ const rgba = await pixels(bank.volume, source);
+ if (source.alphaCoverage !== 0 || rgba.some((a, i) => i % 4 === 3 && a !== 0))
+ throw new TypeError('An omitted component slab contains opacity.');
+ }
+ quads.push(structuredClone(reference));
+ }
const bounds = neutral.frame.boundsUnits, counts = neutral.sampling.sliceCounts;
const slices: VolumeSlices = { quads, boundsUnits: { min: [...bounds.min], max: [...bounds.max] }, provenance: { fieldIdentity: neutral.fieldIdentity }, approximation: {
method: 'Union of registered retained component rasters; no pixel resampling.', radialEmission: 'none', limitations: ['Layout occupancy is the union; display banks retain distinct opacity.'],
- samplesPerSlab: 4, opticalWeight: 1, exposureGain: 1, sliceCounts: counts,
+ samplesPerSlab: neutral.sampling.layerPlan ? neutral.sampling.samplesPerSlab : 4, opticalWeight: 1, exposureGain: 1, sliceCounts: counts,
+ ...(neutral.sampling.layerPlan ? { layerPlan: neutral.sampling.layerPlan } : {}),
slabPitchUnits: { x: (bounds.max[0] - bounds.min[0]) / counts.x, y: (bounds.max[1] - bounds.min[1]) / counts.y, z: (bounds.max[2] - bounds.min[2]) / counts.z } } };
- const canonical = backend.compileVolume({ id: `compiler-${neutral.id}`, frame: neutral.frame, slices });
+ const canonical = backend.compileVolume({ id: `compiler-${neutral.id}`, frame: neutral.frame,
+ slices: neutral.frame.referenceFrame === COMPILER_PHYSICAL_REFERENCE ? compilerPreparedSlices(slices) : slices });
const pins = new Map();
for (const bank of banks) {
const alphaSha256 = bank.alpha.digest('hex');
diff --git a/labs/nebula/packages/volume-bake/src/compiler/retained-material.ts b/labs/nebula/packages/volume-bake/src/compiler/retained-material.ts
index 642f2327bd..39b0f7fc49 100644
--- a/labs/nebula/packages/volume-bake/src/compiler/retained-material.ts
+++ b/labs/nebula/packages/volume-bake/src/compiler/retained-material.ts
@@ -3,11 +3,12 @@ import { readFile, writeFile } from 'node:fs/promises';
import { dirname, isAbsolute, relative } from 'node:path';
import type { Vector3 } from '@cssearth/volume-core/contracts/volume-recipe';
import { containedPath, sha256, verifiedBytes } from '../compact-inputs/density-grid.ts';
-import type { VolumeSlices, VolumeSliceQuad } from '@cssearth/volume-core/contracts/volume-slices';
+import { readVolumeLayerPlan, readVolumeSlabInterval, validateVolumeLayerSlices, type VolumeSlices, type VolumeSliceQuad } from '@cssearth/volume-core/contracts/volume-slices';
import { recolorCloudSlices } from '../slices/material.ts';
import { verifyCompilerAlphaIdentity, type BakeCompilerOptions, type CompilerLensInput } from './bake.ts';
import { readCompilerBakeResult, type CompilerBakeResult, type CompilerLensVolume, type CompilerPin } from '@cssearth/volume-core/contracts/compiler-bake';
import { compilerSlabMaterial } from '@cssearth/volume-core/materials/slab-material';
+import { COMPILER_PHYSICAL_REFERENCE, compilerPreparedSlices } from '@cssearth/volume-core/coordinates/compiler-frame';
export interface RetainedMaterialBankOptions {
root: string; outputDirectory: string; scene: CompilerBakeResult; neutralSlicesPin: CompilerPin;
@@ -44,6 +45,9 @@ function readSlices(v: unknown, scene: CompilerBakeResult): VolumeSlices {
a.displayColorMatrix !== undefined || (a.emissionTransfer !== undefined && a.emissionTransfer !== 'shared-opacity'))
throw new TypeError('Invalid retained compiler slab sampling.');
const counts = a.sliceCounts, pitches = a.slabPitchUnits;
+ const layerPlan = a.layerPlan === undefined ? undefined : readVolumeLayerPlan(a.layerPlan);
+ if (JSON.stringify(layerPlan) !== JSON.stringify(scene.sampling.layerPlan))
+ throw new TypeError('Retained compiler layer plan differs from the scene sampling.');
for (const [index, axis] of (['x', 'y', 'z'] as const).entries()) {
const count = counts[axis], pitch = pitches[axis];
if (count !== scene.sampling.sliceCounts[axis] || !integer(count) || !positive(pitch) ||
@@ -53,12 +57,16 @@ function readSlices(v: unknown, scene: CompilerBakeResult): VolumeSlices {
v.quads.some(q => q.axis === axis && q.sliceIndex >= count)) throw new TypeError('Invalid retained compiler axis sampling.');
}
// Each runtime-owned property has been checked above; optional noncompiler transport modes are rejected.
- return { quads: v.quads, boundsUnits: { min: v.boundsUnits.min, max: v.boundsUnits.max }, provenance: v.provenance,
+ const slices: VolumeSlices = { quads: v.quads.map(q => ({ ...q, ...(q.slab === undefined ? {} : { slab: readVolumeSlabInterval(q.slab) }) })),
+ boundsUnits: { min: v.boundsUnits.min, max: v.boundsUnits.max }, provenance: v.provenance,
approximation: { method: a.method, radialEmission: a.radialEmission, limitations: a.limitations,
samplesPerSlab: a.samplesPerSlab, opticalWeight: a.opticalWeight, exposureGain: a.exposureGain,
...(a.emissionTransfer === 'shared-opacity' ? { emissionTransfer: a.emissionTransfer } : {}),
+ ...(layerPlan ? { layerPlan } : {}),
sliceCounts: { ...scene.sampling.sliceCounts }, slabPitchUnits: {
x: Number(pitches.x), y: Number(pitches.y), z: Number(pitches.z) } } };
+ validateVolumeLayerSlices(slices);
+ return slices;
}
const geometry = (s: VolumeSlices) => s.quads.map(({ texturePath: _p, sha256: _h, bytes: _b, ...q }) => q);
@@ -84,7 +92,7 @@ export async function prepareRetainedMaterialBank(options: RetainedMater
backend.assertBankIdentity(neutral, scene);
const slices = readSlices(JSON.parse((await verifiedBytes(root, neutralSlicesPin)).toString('utf8')), scene);
const compile = (bank: VolumeSlices) => backend.compileVolume({ id: `compiler-${scene.volumeId ?? scene.id}`,
- frame: scene.frame, slices: bank });
+ frame: scene.frame, slices: scene.frame.referenceFrame === COMPILER_PHYSICAL_REFERENCE ? compilerPreparedSlices(bank) : bank });
backend.assertLensGeometry(neutral, compile(slices), scene, {});
await verifyCompilerAlphaIdentity(scene.alphaSha256, sourceDirectory, slices);
const origin = scene.coordinates.localOriginArcsec;
diff --git a/labs/nebula/packages/volume-bake/src/slices/emission-layout.test.ts b/labs/nebula/packages/volume-bake/src/slices/emission-layout.test.ts
new file mode 100644
index 0000000000..d77d88a53f
--- /dev/null
+++ b/labs/nebula/packages/volume-bake/src/slices/emission-layout.test.ts
@@ -0,0 +1,90 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { mkdtemp, readFile, rm } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+import { createHash } from 'node:crypto';
+import { bakeMasterVolumeSlices, type MasterVolumeOptions } from './emission.ts';
+import sharp from 'sharp';
+import { recolorCloudSlices } from './material.ts';
+import { compilerSlabMaterial } from '@cssearth/volume-core/materials/slab-material';
+import { readVolumeLayerPlan, validateVolumeLayerSlices, type VolumeLayerPlan } from '@cssearth/volume-core/contracts/volume-slices';
+
+function plan(): VolumeLayerPlan {
+ return { schema: 'cssearth-volume-layer-plan@1', referenceSliceCounts: { x: 4, y: 4, z: 4 }, referenceSamplesPerSlab: 4,
+ axes: { x: [{ startCell: 0, endCell: 1 }, { startCell: 1, endCell: 4 }],
+ y: [{ startCell: 0, endCell: 2 }, { startCell: 2, endCell: 4 }],
+ z: [{ startCell: 0, endCell: 3 }, { startCell: 3, endCell: 4 }] } };
+}
+
+async function fixture(t: { after(fn: () => Promise): void }): Promise {
+ const directory = await mkdtemp(join(tmpdir(), 'volume-layer-test-'));
+ t.after(() => rm(directory, { recursive: true, force: true }));
+ return { boundsKpc: { min: [-1, -2, -3], max: [2, 2, 3] }, sliceCounts: { x: 3, y: 4, z: 5 },
+ samplesPerSlab: 4, exposureGain: .7, masterWidth: 4, masterDirectory: join(directory, 'master'),
+ deliveryBanks: [], unitsPerSourceUnit: 7, provenance: { fixture: 'legacy reference before optional layer layouts' }, onProgress() {},
+ sampleEmission(x, y, z, out) { out[0] = .1 + (x + 1) / 8; out[1] = .1 + (y + 2) / 9; out[2] = .1 + (z + 3) / 10; } };
+}
+
+test('omitted layer layout preserves the original uniform manifest and PNG bytes', async t => {
+ const options = await fixture(t), { masters } = await bakeMasterVolumeSlices(options);
+ const digest = createHash('sha256').update(await readFile(join(options.masterDirectory, 'volume-slices.json')));
+ for (const q of masters.quads) digest.update(await readFile(join(options.masterDirectory, q.texturePath)));
+ assert.equal(digest.digest('hex'), 'b41ebde649d79c33e72c79771610b82725b8d91688866727db5f800ea1801fb7');
+ assert.equal(masters.quads.length, 12);
+});
+
+test('nonuniform intervals retain every reference midpoint and paint the actual integrated color', async t => {
+ const options = await fixture(t), zSamples: number[] = [];
+ Object.assign(options, { boundsKpc: { min: [0, 0, 0], max: [4, 4, 4] }, sliceCounts: { x: 2, y: 2, z: 2 },
+ layerPlan: plan(), masterWidth: 2, unitsPerSourceUnit: 1, exposureGain: 1 });
+ options.sampleEmission = (x, y, z, out) => { out.fill(.25); if (x === 1 && y === 3) zSamples.push(z); };
+ const { masters } = await bakeMasterVolumeSlices(options);
+ assert.deepEqual(zSamples, Array.from({ length: 16 }, (_, i) => (i + .5) / 4), 'No reference depth sample is dropped or shifted.');
+ assert.deepEqual(masters.quads.find(q => q.id === 'z-0')!.slab, { start: 0, end: 3, samples: 12, startCell: 0, endCell: 3 });
+ assert.equal(masters.quads.find(q => q.id === 'z-0')!.center[2], 1.5);
+ assert.equal(masters.approximation.slabPitchUnits.z, 2, 'Mean pitch is descriptive, not the material integration width.');
+ assert.deepEqual(validateVolumeLayerSlices(masters), plan());
+ const sample = compilerSlabMaterial((_x, _y, _z, out) => out.fill(.25), (_x, _y, z, out) => {
+ out[0] = z < 1 ? 255 : 0; out[1] = 0; out[2] = z < 1 ? 0 : 255; return true;
+ });
+ const outputDirectory = join(options.masterDirectory, '..', 'painted');
+ const { slices } = await recolorCloudSlices({ slices: masters, loadResource: path => readFile(join(options.masterDirectory, path)),
+ outputDirectory, sampleImageRgb: sample, preserveMaterialIntensity: true, encoding: { format: 'png' } });
+ for (const [id, expected] of [['z-0', [85, 0, 170]], ['z-1', [0, 0, 255]]] as const) {
+ const q = slices.quads.find(q => q.id === id)!, original = masters.quads.find(q => q.id === id)!;
+ const pixels = await sharp(await readFile(join(outputDirectory, q.texturePath))).raw().toBuffer();
+ const neutral = await sharp(await readFile(join(options.masterDirectory, original.texturePath))).raw().toBuffer();
+ assert.deepEqual([...pixels.subarray(0, 3)], expected, 'Material follows the true interval, not the mean pitch.');
+ assert.equal(pixels[3], neutral[3]);
+ assert.deepEqual(q.slab, original.slab);
+ }
+ const broken = structuredClone(masters);
+ delete broken.quads[0]!.slab;
+ await assert.rejects(recolorCloudSlices({ slices: broken, loadResource: async () => { throw new Error('Must validate before reading bytes'); },
+ outputDirectory, sampleImageRgb: sample }), /slab interval/);
+ const changed = structuredClone(masters); changed.quads[0]!.slab!.end += .25;
+ assert.throws(() => validateVolumeLayerSlices(changed), /differ from/);
+ const unmarked = structuredClone(masters); delete unmarked.approximation.layerPlan;
+ assert.throws(() => validateVolumeLayerSlices(unmarked), /require a retained/);
+});
+
+test('layer plans reject gaps, overlaps, nonfinite values, excess layers and excessive sample counts', async t => {
+ for (const mutate of [
+ (p: VolumeLayerPlan) => { p.axes.x = [{ startCell: 1, endCell: 4 }]; },
+ (p: VolumeLayerPlan) => { p.axes.x = [{ startCell: 0, endCell: 2 }, { startCell: 1, endCell: 4 }]; },
+ (p: VolumeLayerPlan) => { p.axes.x = [{ startCell: 0, endCell: 3 }]; },
+ (p: VolumeLayerPlan) => { p.axes.x = [{ startCell: 0, endCell: Infinity }]; },
+ (p: VolumeLayerPlan) => { p.referenceSamplesPerSlab = NaN; },
+ (p: VolumeLayerPlan) => { p.referenceSliceCounts.x = 512; p.referenceSamplesPerSlab = 16; p.axes.x = [{ startCell: 0, endCell: 512 }]; },
+ (p: VolumeLayerPlan) => { for (const axis of ['x', 'y', 'z'] as const) {
+ p.referenceSliceCounts[axis] = 200; p.axes[axis] = Array.from({ length: 200 }, (_, i) => ({ startCell: i, endCell: i + 1 })); } },
+ ]) { const p = plan(); mutate(p); assert.throws(() => readVolumeLayerPlan(p), TypeError); }
+ const bounded = plan(); bounded.referenceSliceCounts.x = 512; bounded.referenceSamplesPerSlab = 8;
+ bounded.axes.x = [{ startCell: 0, endCell: 512 }];
+ assert.equal(readVolumeLayerPlan(bounded).axes.x.length, 1, '4096 samples remain a valid bounded full-axis merge.');
+ const options = await fixture(t); options.layerPlan = plan();
+ await assert.rejects(bakeMasterVolumeSlices(options), /must match/);
+ options.sliceCounts = { x: 2, y: 2, z: 2 }; options.unitsPerSourceUnit = Number.MAX_VALUE;
+ await assert.rejects(bakeMasterVolumeSlices(options), /physical intervals/);
+});
diff --git a/labs/nebula/packages/volume-bake/src/slices/emission.ts b/labs/nebula/packages/volume-bake/src/slices/emission.ts
index 8954d8ba55..cee81aef11 100644
--- a/labs/nebula/packages/volume-bake/src/slices/emission.ts
+++ b/labs/nebula/packages/volume-bake/src/slices/emission.ts
@@ -3,7 +3,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import sharp from 'sharp';
import type { Axis, Bounds3, Vector3, VolumeImageEncoding } from '@cssearth/volume-core/contracts/volume-recipe';
-import type { VolumeSlices, VolumeSliceQuad } from '@cssearth/volume-core/contracts/volume-slices';
+import { readVolumeLayerPlan, type VolumeLayerPlan, type VolumeSlices, type VolumeSliceQuad } from '@cssearth/volume-core/contracts/volume-slices';
import { encodeVolumeRaster } from './raster.ts';
import { containedPath, sha256 } from '../compact-inputs/density-grid.ts';
@@ -22,6 +22,8 @@ export interface MasterVolumeOptions {
boundsKpc: Bounds3;
sliceCounts: Record;
samplesPerSlab: number;
+ /** Optional grouped reference cells; omitted preserves historical uniform bytes. */
+ layerPlan?: VolumeLayerPlan;
exposureGain: number;
masterWidth: number;
masterDirectory: string;
@@ -91,12 +93,20 @@ export async function bakeMasterVolumeSlices(options: MasterVolumeOptions): Prom
integer(width, 'Master width', 8192); integer(samples, 'Samples per slab', 1024);
positive(options.exposureGain, 'Exposure gain'); positive(options.unitsPerSourceUnit, 'Geometry scale');
for (const axis of axes) integer(counts[axis], 'Slice count', 512);
+ const layerPlan = options.layerPlan === undefined ? undefined : readVolumeLayerPlan(options.layerPlan);
+ if (layerPlan && (samples !== layerPlan.referenceSamplesPerSlab || axes.some(axis => counts[axis] !== layerPlan.axes[axis].length)))
+ throw new TypeError('Retained slice counts and reference samples must match the volume layer plan.');
checkBanks(options.masterDirectory, options.deliveryBanks);
for (const bank of options.deliveryBanks) if (bank.width > width) throw new TypeError('Delivery must not upscale the master.');
const scale = options.unitsPerSourceUnit;
const boundsUnits: Bounds3 = {
min: bounds.min.map(v => v * scale) as Vector3, max: bounds.max.map(v => v * scale) as Vector3,
};
+ if (layerPlan && axes.some((axis, i) => !Number.isFinite(bounds.max[i]! - bounds.min[i]!) ||
+ !Number.isFinite(boundsUnits.min[i]) || !Number.isFinite(boundsUnits.max[i]) ||
+ !(boundsUnits.max[i]! > boundsUnits.min[i]!) ||
+ !((bounds.max[i]! - bounds.min[i]!) / layerPlan.referenceSliceCounts[axis] / samples > 0)))
+ throw new TypeError('Volume layer physical intervals and reference sample spacing must be finite and positive.');
const masters: VolumeSlices = { quads: [], boundsUnits, provenance: options.provenance, approximation: {
method: `Direct XYZ emissivity samples per kpc; shared exponential opacity and optical RGB ratios; lossless full-extent RGBA8 masters. Quantization: ${MASTER_QUANTIZATION}.`,
radialEmission: 'Provided entirely by the authored emissivity sampler.',
@@ -108,6 +118,11 @@ export async function bakeMasterVolumeSlices(options: MasterVolumeOptions): Prom
samplesPerSlab: samples, opticalWeight: 1, exposureGain: options.exposureGain,
emissionTransfer: 'shared-opacity', sliceCounts: { ...counts }, slabPitchUnits: { x: 0, y: 0, z: 0 },
} };
+ if (layerPlan) {
+ masters.approximation.layerPlan = layerPlan;
+ masters.approximation.method += ' Variable slabs merge contiguous reference cells without thinning depth quadrature.';
+ masters.approximation.limitations.push('Nonuniform slab planes sit at interval midpoints; slabPitchUnits is the axis mean, samplesPerSlab is the reference-cell count. Each quad records its actual interval and sample count. Merging preserves reference quadrature, not continuous depth placement or axis-handoff accuracy.');
+ }
const total = counts.x + counts.y + counts.z;
for (const axis of axes) {
const axial = axis === 'x' ? 0 : axis === 'y' ? 1 : 2;
@@ -116,7 +131,9 @@ export async function bakeMasterVolumeSlices(options: MasterVolumeOptions): Prom
const vMax = bounds.max[vertical], vSpan = vMax - bounds.min[vertical];
const height = Math.max(1, Math.round(width * vSpan / uSpan));
dimensions(width, height);
- const pitch = (bounds.max[axial] - bounds.min[axial]) / counts[axis], ds = pitch / samples;
+ const pitch = (bounds.max[axial] - bounds.min[axial]) / counts[axis];
+ const referencePitch = layerPlan ? (bounds.max[axial] - bounds.min[axial]) / layerPlan.referenceSliceCounts[axis] : pitch;
+ const ds = referencePitch / samples;
masters.approximation.slabPitchUnits[axis] = pitch * scale;
await mkdir(resolve(options.masterDirectory, 'slices', axis), { recursive: true });
const rgb: Vector3 = [0, 0, 0];
@@ -125,8 +142,18 @@ export async function bakeMasterVolumeSlices(options: MasterVolumeOptions): Prom
// RGB residuals also conserve faint hue when adjacent slabs differ in color.
const opticalError = new Float64Array(width * height * 3);
for (let index = 0; index < counts[axis]; index++) {
- const depth = bounds.min[axial] + (index + 0.5) * pitch;
- const depths = Float64Array.from({ length: samples }, (_, sample) => depth + pitch * ((sample + 0.5) / samples - 0.5));
+ const group = layerPlan?.axes[axis][index];
+ const start = group ? bounds.min[axial] + group.startCell * referencePitch : 0;
+ const end = group ? bounds.min[axial] + group.endCell * referencePitch : 0;
+ if (group && (!(end > start) || !Number.isFinite(start) || !Number.isFinite(end) ||
+ !(end * scale > start * scale))) throw new TypeError('Volume layer physical intervals must be finite and increasing.');
+ const depth = group ? (start + end) / 2 : bounds.min[axial] + (index + 0.5) * pitch;
+ const sampleCount = group ? (group.endCell - group.startCell) * samples : samples;
+ const depths = Float64Array.from({ length: sampleCount }, (_, sample) => {
+ if (!group) return depth + pitch * ((sample + 0.5) / samples - 0.5);
+ const cell = group.startCell + Math.floor(sample / samples), sub = sample % samples;
+ return bounds.min[axial] + (cell + 0.5) * referencePitch + referencePitch * ((sub + 0.5) / samples - 0.5);
+ });
const rgba = Buffer.alloc(width * height * 4);
let nonzero = 0;
for (let row = 0; row < height; row++) {
@@ -134,7 +161,7 @@ export async function bakeMasterVolumeSlices(options: MasterVolumeOptions): Prom
for (let col = 0; col < width; col++) {
const u = us[col]!;
let red = 0, green = 0, blue = 0;
- for (let sample = 0; sample < samples; sample++) {
+ for (let sample = 0; sample < sampleCount; sample++) {
const d = depths[sample]!;
if (axis === 'x') options.sampleEmission(d, u, v, rgb);
else if (axis === 'y') options.sampleEmission(u, d, v, rgb);
@@ -178,7 +205,8 @@ export async function bakeMasterVolumeSlices(options: MasterVolumeOptions): Prom
vertices, uvs: [[0, 0], [1, 0], [1, 1], [0, 1]],
center: point(axis, depth * scale, (uMin + uSpan / 2) * scale, (vMax - vSpan / 2) * scale),
normal: axis === 'x' ? [-1, 0, 0] : axis === 'y' ? [0, 1, 0] : [0, 0, -1],
- sha256: sha256(bytes), bytes: bytes.length, alphaCoverage: nonzero / (width * height) });
+ sha256: sha256(bytes), bytes: bytes.length, alphaCoverage: nonzero / (width * height),
+ ...(group ? { slab: { start: start * scale, end: end * scale, samples: sampleCount, startCell: group.startCell, endCell: group.endCell } } : {}) });
report(options.onProgress, { phase: 'master', axis, sliceIndex: index, completed: masters.quads.length, total, width });
}
}
diff --git a/labs/nebula/packages/volume-bake/src/slices/material.ts b/labs/nebula/packages/volume-bake/src/slices/material.ts
index 80dfc5903c..aa10d9d24c 100644
--- a/labs/nebula/packages/volume-bake/src/slices/material.ts
+++ b/labs/nebula/packages/volume-bake/src/slices/material.ts
@@ -5,7 +5,8 @@ import sharp from 'sharp';
import { encodeVolumeRaster } from './raster.ts';
import { containedPath, sha256 } from '../compact-inputs/density-grid.ts';
import type { VolumeImageEncoding, Vector3 } from '@cssearth/volume-core/contracts/volume-recipe';
-import type { VolumeSlices, VolumeSliceQuad } from '@cssearth/volume-core/contracts/volume-slices';
+import { validateVolumeLayerSlices, type VolumeSlices, type VolumeSliceQuad } from '@cssearth/volume-core/contracts/volume-slices';
+import type { SlabMaterialSampling } from '@cssearth/volume-core/materials/slab-material';
import { parseCloudAppearance, type CloudAppearance } from '@cssearth/volume-core/materials/cloud-appearance';
export const CLOUD_MATERIAL_METHOD = 'density-opacity-image-material@2';
@@ -20,7 +21,7 @@ export interface CloudMaterialOptions {
slices: VolumeSlices;
loadResource(path: string): Promise;
/** Physical coordinates in the unchanged quad units. RGB is [0,255]; false means outside image coverage. */
- sampleImageRgb(x: number, y: number, z: number, out: Vector3, slab: { axis: 'x' | 'y' | 'z'; pitch: number; samples: number }): boolean;
+ sampleImageRgb(x: number, y: number, z: number, out: Vector3, slab: SlabMaterialSampling): boolean;
appearance?: CloudAppearance;
/** A mixture of normalized 3D component colors already has a physical RGB weight; do not boost its peak again. */
preserveMaterialIntensity?: boolean;
@@ -48,10 +49,25 @@ export async function recolorCloudSlices(options: CloudMaterialOptions): Promise
(!Number.isInteger(encoding.quality) || encoding.quality < 1 || encoding.quality > 100)))
throw new TypeError('Cloud material requires PNG or valid-quality WebP.');
if (!options.slices.quads.length) throw new TypeError('Cloud material requires an accepted slice bank.');
+ const layerPlan = validateVolumeLayerSlices(options.slices);
const coverage: CloudMaterialCoverage = { positiveAlphaTexels: 0, recoloredTexels: 0,
outsideImageTexels: 0, blackImageTexels: 0, preservedReferenceTexels: 0 };
const quads: VolumeSliceQuad[] = [], outputPaths = new Set(), rgb: Vector3 = [0, 0, 0];
for (const quad of options.slices.quads) {
+ const slab: SlabMaterialSampling = { axis: quad.axis, pitch: options.slices.approximation.slabPitchUnits[quad.axis],
+ samples: options.slices.approximation.samplesPerSlab };
+ if (layerPlan) {
+ const interval = quad.slab!, axial = quad.axis === 'x' ? 0 : quad.axis === 'y' ? 1 : 2;
+ const min = options.slices.boundsUnits.min[axial]!, max = options.slices.boundsUnits.max[axial]!;
+ const referencePitch = (max - min) / layerPlan.referenceSliceCounts[quad.axis], samples = layerPlan.referenceSamplesPerSlab;
+ slab.pitch = interval.end - interval.start;
+ slab.samples = interval.samples;
+ slab.sampleSpacing = referencePitch / samples;
+ slab.sampleOffsets = Array.from({ length: interval.samples }, (_, i) => {
+ const cell = interval.startCell + Math.floor(i / samples), sub = i % samples;
+ return min + (cell + .5) * referencePitch + referencePitch * ((sub + .5) / samples - .5) - quad.center[axial]!;
+ });
+ }
const input = Buffer.from(await options.loadResource(quad.texturePath));
if (input.length !== quad.bytes || sha256(input) !== quad.sha256) throw new TypeError(`Accepted cloud texture changed: ${quad.texturePath}.`);
const { data: source, info } = await sharp(input).ensureAlpha().raw({ depth: 'uchar' }).toBuffer({ resolveWithObject: true });
@@ -71,8 +87,7 @@ export async function recolorCloudSlices(options: CloudMaterialOptions): Promise
const y = origin[1] + u * (horizontal[1] - origin[1]) + v * (vertical[1] - origin[1]);
const z = origin[2] + u * (horizontal[2] - origin[2]) + v * (vertical[2] - origin[2]);
rgb[0] = rgb[1] = rgb[2] = 0;
- if (!options.sampleImageRgb(x, y, z, rgb, { axis: quad.axis, pitch: options.slices.approximation.slabPitchUnits[quad.axis],
- samples: options.slices.approximation.samplesPerSlab })) { coverage.outsideImageTexels++; continue; }
+ if (!options.sampleImageRgb(x, y, z, rgb, slab)) { coverage.outsideImageTexels++; continue; }
if (rgb.some(value => !Number.isFinite(value) || value < 0 || value > 255))
throw new TypeError('Cloud image samples must be finite RGB in [0,255].');
const peak = Math.max(...rgb);
diff --git a/labs/nebula/packages/volume-core/package.json b/labs/nebula/packages/volume-core/package.json
index ed0c1a15da..60b31a9b06 100644
--- a/labs/nebula/packages/volume-core/package.json
+++ b/labs/nebula/packages/volume-core/package.json
@@ -15,6 +15,7 @@
"./contracts/observation-mapping": "./src/contracts/observation-mapping.ts",
"./contracts/observation-photo": "./src/contracts/observation-photo.ts",
"./contracts/prepared-catalogue-stars": "./src/contracts/prepared-catalogue-stars.ts",
+ "./contracts/render-element-budget": "./src/contracts/render-element-budget.ts",
"./contracts/sampled-emission-fit": "./src/contracts/sampled-emission-fit.ts",
"./contracts/sampled-recipe": "./src/contracts/sampled-recipe.ts",
"./contracts/shape-scene": "./src/contracts/shape-scene.ts",
@@ -50,7 +51,10 @@
"./materials/slab-material": "./src/materials/slab-material.ts",
"./materials/star-photometry": "./src/materials/star-photometry.ts",
"./package.json": "./package.json",
- "./sampling/registered-raster": "./src/sampling/registered-raster.ts"
+ "./sampling/registered-raster": "./src/sampling/registered-raster.ts",
+ "./sampling/layer-optimization": "./src/sampling/layer-optimization.ts",
+ "./fields/photometric-mge": "./src/fields/photometric-mge.ts",
+ "./fields/photometric-emission": "./src/fields/photometric-emission.ts"
},
"scripts": {
"typecheck": "tsc --noEmit -p tsconfig.json"
diff --git a/labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts b/labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts
index f5487e5507..c5ba07b52f 100644
--- a/labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts
+++ b/labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts
@@ -1,5 +1,8 @@
import type { DensityVolumeFrame } from './volume-frame.ts';
import type { EmissionBounds, EmissionVector3, SkyBounds } from './emission.ts';
+import { readVolumeLayerPlan, type VolumeLayerPlan } from './volume-slices.ts';
+import { readLayerOptimizationReport, type LayerOptimizationReport } from '../sampling/layer-optimization.ts';
+import { readRenderElementBudget, type RenderElementBudget } from './render-element-budget.ts';
export const COMPILER_LONGEST_AXIS_SLICES = 512;
@@ -15,7 +18,7 @@ export interface CompilerLensVolume {
export interface CompilerStarMaterial { rgb: [number, number, number]; diameterUnits: number; alpha: number }
export interface PreparedCompilerStar {
id: string;
- /** Centered west/north/away coordinates in the scene's arcsecond units. */
+ /** Centered coordinates in frame.referenceFrame; current preparations use west/north/toward. */
positionUnits: EmissionVector3;
rgb: [number, number, number];
/** Historical fixed-screen markers; newly prepared stars use angular scene units. */
@@ -56,7 +59,8 @@ export interface CompilerBakeResult {
stars: PreparedCompilerStar[];
starSprites?: CompilerStarSprites;
alphaSha256: string;
- sampling: { sliceCounts: { x: number; y: number; z: number }; imageWidth: 512; samplesPerSlab: 4 };
+ sampling: { sliceCounts: { x: number; y: number; z: number }; imageWidth: 512; samplesPerSlab: 4;
+ layerPlan?: VolumeLayerPlan; layerOptimization?: LayerOptimizationReport; renderBudget?: RenderElementBudget };
}
const record = (v: unknown): v is Record => v !== null && typeof v === 'object' && !Array.isArray(v);
@@ -137,7 +141,7 @@ export function readCompilerBakeResult(value: unknown): CompilerBakeResult {
const local = { min: bounds.min.map((n, i) => n - origin[i]!), max: bounds.max.map((n, i) => n - origin[i]!) };
const frame = value.frame, coordinates = value.coordinates, sampling = value.sampling;
const counts = sampling.sliceCounts;
- if (frame.referenceFrame !== 'lab-sky-angular' || frame.epochJdTt !== 2451545 || frame.metersPerUnit !== 1 ||
+ if (!['lab-sky-angular', 'lab-sky-west-north-toward'].includes(String(frame.referenceFrame)) || frame.epochJdTt !== 2451545 || frame.metersPerUnit !== 1 ||
!same(frame.originM, [0, 0, 0]) || !same(frame.localToReferenceXyzw, [0, 0, 0, 1]) || !bounds3(frame.boundsUnits) ||
!same(frame.boundsUnits.min, local.min) || !same(frame.boundsUnits.max, local.max) ||
!Array.isArray(coordinates.axes) || coordinates.axes.join(',') !== 'west,north,away' ||
@@ -145,10 +149,19 @@ export function readCompilerBakeResult(value: unknown): CompilerBakeResult {
sampling.imageWidth !== 512 || sampling.samplesPerSlab !== 4 || !record(counts) ||
['x', 'y', 'z'].some(axis => !Number.isInteger(counts[axis]) || Number(counts[axis]) < 1 || Number(counts[axis]) > COMPILER_LONGEST_AXIS_SLICES))
throw new TypeError('Compiler bake frame or sampling is invalid.');
+ if (sampling.layerPlan !== undefined) {
+ const plan = readVolumeLayerPlan(sampling.layerPlan);
+ if (plan.referenceSamplesPerSlab !== sampling.samplesPerSlab ||
+ (['x', 'y', 'z'] as const).some(axis => plan.axes[axis].length !== counts[axis]))
+ throw new TypeError('Compiler grouped sampling differs from its retained layer plan.');
+ if (sampling.layerOptimization !== undefined) readLayerOptimizationReport(sampling.layerOptimization, plan);
+ } else if (sampling.layerOptimization !== undefined) throw new TypeError('Compiler layer optimization report requires its retained plan.');
const expectedSpan = Math.max(value.skyBoundsArcsec.max[0] - value.skyBoundsArcsec.min[0], value.skyBoundsArcsec.max[1] - value.skyBoundsArcsec.min[1]);
if (Math.abs(value.spanArcsec - expectedSpan) > 1e-10) throw new TypeError('Compiler source span differs from its sky bounds.');
if (!Array.isArray(value.lenses) || value.lenses.length < 1 || value.lenses.length > 8 ||
!Array.isArray(value.stars) || value.stars.length > 5000) throw new TypeError('Compiler bake collections are invalid.');
+ if (sampling.renderBudget !== undefined) readRenderElementBudget(sampling.renderBudget, value.stars.length,
+ Number(counts.x) + Number(counts.y) + Number(counts.z));
const lensIds = new Set();
for (const item of value.lenses) {
if (!record(item)) throw new TypeError('Invalid compiler lens volume.');
diff --git a/labs/nebula/packages/volume-core/src/contracts/emission.ts b/labs/nebula/packages/volume-core/src/contracts/emission.ts
index ccb6eb92ea..b9f986c17e 100644
--- a/labs/nebula/packages/volume-core/src/contracts/emission.ts
+++ b/labs/nebula/packages/volume-core/src/contracts/emission.ts
@@ -1,3 +1,4 @@
+import type { RetainedPhotometricEnvelope } from '../fields/photometric-emission.ts';
import type { JointParameters } from './joint-parameters.ts';
import type { CompilerControls } from './compiler-controls.ts';
import type { EmissionWindow } from '../fields/emission-window.ts';
@@ -38,6 +39,7 @@ export interface EmissionFieldModel {
controls: CompilerControls; components: EmissionComponent[]; bounds: EmissionBounds;
skyBounds: SkyBounds; scaffold: JointParameters | null;
emissionWindow?: EmissionWindow;
+ photometricEnvelope?: RetainedPhotometricEnvelope;
depthConstraints?: {
recipeId: string; evidenceSha256: string; paperGuidedComponents: number; authoredComponents: number;
assignments: { componentId: string; featureId: string; methodId: string; evidenceIds: string[]; support: string }[];
diff --git a/labs/nebula/packages/volume-core/src/contracts/render-element-budget.test.ts b/labs/nebula/packages/volume-core/src/contracts/render-element-budget.test.ts
new file mode 100644
index 0000000000..b78828c3f0
--- /dev/null
+++ b/labs/nebula/packages/volume-core/src/contracts/render-element-budget.test.ts
@@ -0,0 +1,28 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { createRenderElementBudget, maximumRenderSlabs, readRenderElementBudget, renderElementCount, type RenderElementProfile } from './render-element-budget.ts';
+
+const profile: RenderElementProfile = { schema: 'cssearth-render-element-profile@1', id: 'test-retained-renderer@1',
+ maximumElements: 500, elementsPerSlab: 3, elementsPerStar: 1, reservedElements: 47 };
+
+test('all retained stars, optical copies and wrappers consume the same element budget', () => {
+ assert.equal(maximumRenderSlabs(profile, 0), 151);
+ assert.equal(maximumRenderSlabs(profile, 3), 150);
+ assert.equal(createRenderElementBudget(profile, 0, 151).totalElements, 500);
+ assert.equal(createRenderElementBudget(profile, 3, 150).totalElements, 500);
+ assert.throws(() => createRenderElementBudget(profile, 1, 151), /501 retained elements/);
+ assert.throws(() => createRenderElementBudget(profile, 0, 152), /503 retained elements/);
+ assert.throws(() => maximumRenderSlabs(profile, 445), /no complete XYZ volume/);
+ assert.equal(renderElementCount(profile, 0, 459), 1424, 'Historical over-budget cost remains reportable.');
+});
+
+test('a saved budget rejects changed geometry, hidden-point omissions and forged counters', () => {
+ const saved = createRenderElementBudget(profile, 3, 150);
+ assert.deepEqual(readRenderElementBudget(saved, 3, 150, profile), saved);
+ assert.deepEqual(readRenderElementBudget(saved, 0, 150, profile), saved, 'A material component may reserve the final shared stars.');
+ assert.throws(() => readRenderElementBudget(saved, 4, 150, profile), /star reservation/);
+ assert.throws(() => readRenderElementBudget(saved, 3, 151, profile), /retained elements/);
+ assert.throws(() => readRenderElementBudget({ ...saved, totalElements: 499 }, 3, 150, profile), /differs/);
+ assert.throws(() => readRenderElementBudget(saved, 3, 150, { ...profile, elementsPerSlab: 4 }), /host renderer/);
+ assert.throws(() => maximumRenderSlabs(profile, -1), /nonnegative/);
+});
diff --git a/labs/nebula/packages/volume-core/src/contracts/render-element-budget.ts b/labs/nebula/packages/volume-core/src/contracts/render-element-budget.ts
new file mode 100644
index 0000000000..259bf0e527
--- /dev/null
+++ b/labs/nebula/packages/volume-core/src/contracts/render-element-budget.ts
@@ -0,0 +1,59 @@
+/** Platform-neutral accounting. The host supplies its tested retained-DOM cost profile. */
+export interface RenderElementProfile {
+ schema: 'cssearth-render-element-profile@1';
+ id: string;
+ maximumElements: number;
+ elementsPerSlab: number;
+ elementsPerStar: number;
+ reservedElements: number;
+}
+export interface RenderElementBudget {
+ schema: 'cssearth-render-element-budget@1';
+ profile: RenderElementProfile;
+ starCount: number;
+ slabCount: number;
+ maximumSlabs: number;
+ totalElements: number;
+}
+const record = (value: unknown): value is Record => value !== null && typeof value === 'object' && !Array.isArray(value);
+const integer = (value: unknown, minimum: number): value is number => typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum;
+export function readRenderElementProfile(value: unknown): RenderElementProfile {
+ if (!record(value) || value.schema !== 'cssearth-render-element-profile@1' || typeof value.id !== 'string' || !value.id ||
+ !integer(value.maximumElements, 1) || !integer(value.elementsPerSlab, 1) || !integer(value.elementsPerStar, 1) ||
+ !integer(value.reservedElements, 0) || value.reservedElements >= value.maximumElements)
+ throw new TypeError('Invalid retained render-element profile.');
+ return { schema: value.schema, id: value.id, maximumElements: value.maximumElements, elementsPerSlab: value.elementsPerSlab,
+ elementsPerStar: value.elementsPerStar, reservedElements: value.reservedElements };
+}
+/** Counts hidden retained elements too; no stars are selected or removed here. */
+export function renderElementCount(profile: RenderElementProfile, starCount: number, slabCount: number): number {
+ const cost = readRenderElementProfile(profile);
+ if (!integer(starCount, 0) || !integer(slabCount, 0)) throw new TypeError('Render-element counts must be nonnegative safe integers.');
+ const count = cost.reservedElements + starCount * cost.elementsPerStar + slabCount * cost.elementsPerSlab;
+ if (!Number.isSafeInteger(count)) throw new TypeError('Render-element count exceeds exact integer range.');
+ return count;
+}
+export function maximumRenderSlabs(profile: RenderElementProfile, starCount: number): number {
+ const cost = readRenderElementProfile(profile), reserved = renderElementCount(cost, starCount, 0);
+ const maximum = Math.floor((cost.maximumElements - reserved) / cost.elementsPerSlab);
+ if (maximum < 3) throw new RangeError(`Retained stars and renderer overhead leave no complete XYZ volume within ${cost.maximumElements} elements.`);
+ return maximum;
+}
+export function createRenderElementBudget(profile: RenderElementProfile, starCount: number, slabCount: number): RenderElementBudget {
+ const cost = readRenderElementProfile(profile), maximumSlabs = maximumRenderSlabs(cost, starCount);
+ const totalElements = renderElementCount(cost, starCount, slabCount);
+ if (slabCount < 3 || slabCount > maximumSlabs) throw new RangeError(`Prepared volume needs ${totalElements} retained elements; limit is ${cost.maximumElements}.`);
+ return { schema: 'cssearth-render-element-budget@1', profile: cost, starCount, slabCount, maximumSlabs, totalElements };
+}
+/** A shared component bank may reserve stars that are added only to the final union scene. */
+export function readRenderElementBudget(value: unknown, actualStars: number, actualSlabs: number, expectedProfile?: RenderElementProfile): RenderElementBudget {
+ if (!record(value) || value.schema !== 'cssearth-render-element-budget@1' || !integer(value.starCount, 0) ||
+ !integer(actualStars, 0) || actualStars > value.starCount) throw new TypeError('Invalid retained render-element budget or star reservation.');
+ const profile = readRenderElementProfile(value.profile);
+ if (expectedProfile && JSON.stringify(profile) !== JSON.stringify(readRenderElementProfile(expectedProfile)))
+ throw new TypeError('Saved render-element profile differs from its host renderer.');
+ const expected = createRenderElementBudget(profile, value.starCount, actualSlabs);
+ if (value.slabCount !== expected.slabCount || value.maximumSlabs !== expected.maximumSlabs || value.totalElements !== expected.totalElements)
+ throw new TypeError('Saved render-element budget differs from its retained scene.');
+ return expected;
+}
diff --git a/labs/nebula/packages/volume-core/src/contracts/volume-slices.ts b/labs/nebula/packages/volume-core/src/contracts/volume-slices.ts
index f872efac38..17fa21a2ca 100644
--- a/labs/nebula/packages/volume-core/src/contracts/volume-slices.ts
+++ b/labs/nebula/packages/volume-core/src/contracts/volume-slices.ts
@@ -1,15 +1,104 @@
import type { Axis, Bounds3, DisplayColorMatrix, Vector3, VolumeRecipe } from './volume-recipe.ts';
+
+/** Contiguous reference cells, including startCell and excluding endCell. */
+export interface VolumeLayerGroup { startCell: number; endCell: number }
+/** Offline thinning merges cells, never their integration samples. */
+export interface VolumeLayerPlan {
+ schema: 'cssearth-volume-layer-plan@1';
+ referenceSliceCounts: Record;
+ referenceSamplesPerSlab: number;
+ axes: Record;
+}
+/** The integrated interval in the same physical units and frame as the quad. */
+export interface VolumeSlabInterval extends VolumeLayerGroup { start: number; end: number; samples: number }
+const axes = ['x', 'y', 'z'] as const;
+const record = (v: unknown): v is Record => !!v && typeof v === 'object' && !Array.isArray(v);
+const integer = (v: unknown, min: number, max: number): v is number =>
+ typeof v === 'number' && Number.isSafeInteger(v) && v >= min && v <= max;
+
+export function readVolumeLayerPlan(value: unknown): VolumeLayerPlan {
+ if (!record(value) || value.schema !== 'cssearth-volume-layer-plan@1' || !record(value.referenceSliceCounts) ||
+ !integer(value.referenceSamplesPerSlab, 1, 1024) || !record(value.axes))
+ throw new TypeError('Invalid volume layer plan reference grid.');
+ const counts: Record = { x: 0, y: 0, z: 0 };
+ const groups: Record = { x: [], y: [], z: [] };
+ let total = 0;
+ for (const axis of axes) {
+ const count = value.referenceSliceCounts[axis], input = value.axes[axis];
+ if (!integer(count, 1, 512) || !Array.isArray(input) || input.length < 1 || input.length > count)
+ throw new TypeError('Invalid volume layer plan axis.');
+ counts[axis] = count;
+ let next = 0;
+ for (const group of input) {
+ if (!record(group) || !integer(group.startCell, 0, count - 1) || !integer(group.endCell, 1, count) ||
+ group.startCell !== next || group.endCell <= group.startCell ||
+ (group.endCell - group.startCell) * value.referenceSamplesPerSlab > 4096)
+ throw new TypeError('Volume layer intervals must partition reference cells and contain at most 4096 samples.');
+ groups[axis].push({ startCell: group.startCell, endCell: group.endCell });
+ next = group.endCell;
+ }
+ if (next !== count) throw new TypeError('Volume layer intervals must cover the full reference extent.');
+ total += input.length;
+ }
+ if (total > 500) throw new TypeError('A volume layer plan must retain at most 500 layers across XYZ.');
+ return { schema: value.schema, referenceSliceCounts: counts, referenceSamplesPerSlab: value.referenceSamplesPerSlab, axes: groups };
+}
+
+export function readVolumeSlabInterval(value: unknown): VolumeSlabInterval {
+ if (!record(value) || typeof value.start !== 'number' || !Number.isFinite(value.start) ||
+ typeof value.end !== 'number' || !Number.isFinite(value.end) || value.end <= value.start ||
+ !integer(value.startCell, 0, 511) || !integer(value.endCell, 1, 512) || value.endCell <= value.startCell ||
+ !integer(value.samples, 1, 4096)) throw new TypeError('Invalid physical volume slab interval.');
+ return { start: value.start, end: value.end, samples: value.samples, startCell: value.startCell, endCell: value.endCell };
+}
+
export interface VolumeSliceQuad {
id: string; axis: Axis; sliceIndex: number; texturePath: string; widthPx: number; heightPx: number;
/** Image top-left first: required by PolyCSS's image/projective backend. */
vertices: [Vector3, Vector3, Vector3, Vector3];
uvs: [[number, number], [number, number], [number, number], [number, number]];
center: Vector3; normal: Vector3; sha256: string; bytes: number; alphaCoverage: number;
+ slab?: VolumeSlabInterval;
}
export interface VolumeSlices {
quads: VolumeSliceQuad[]; boundsUnits: Bounds3; provenance: unknown;
approximation: { method: string; radialEmission: string; limitations: string[];
samplesPerSlab: number; opticalWeight: number; exposureGain: number;
displayColorMatrix?: DisplayColorMatrix; emissionTransfer?: VolumeRecipe['material']['emissionTransfer'];
- sliceCounts: Record; slabPitchUnits: Record };
+ sliceCounts: Record; slabPitchUnits: Record;
+ /** When present, pitch is only the mean; quad.slab defines the actual integration interval. */
+ layerPlan?: VolumeLayerPlan };
+}
+
+/** Validate a retained plan against its physical quads before material replay. */
+export function validateVolumeLayerSlices(slices: VolumeSlices): VolumeLayerPlan | undefined {
+ if (slices.approximation.layerPlan === undefined) {
+ if (slices.quads.some(q => q.slab !== undefined)) throw new TypeError('Physical slab intervals require a retained volume layer plan.');
+ return undefined;
+ }
+ const plan = readVolumeLayerPlan(slices.approximation.layerPlan);
+ if (slices.approximation.samplesPerSlab !== plan.referenceSamplesPerSlab ||
+ slices.quads.length !== axes.reduce((n, axis) => n + plan.axes[axis].length, 0))
+ throw new TypeError('Volume layer sampling differs from its retained plan.');
+ for (const [axial, axis] of axes.entries()) {
+ const min = slices.boundsUnits.min[axial]!, max = slices.boundsUnits.max[axial]!;
+ if (!Number.isFinite(min) || !Number.isFinite(max) || !(max > min)) throw new TypeError('Invalid volume layer bounds.');
+ const pitch = (max - min) / plan.referenceSliceCounts[axis], groups = plan.axes[axis];
+ const quads = slices.quads.filter(q => q.axis === axis);
+ if (slices.approximation.sliceCounts[axis] !== groups.length || quads.length !== groups.length ||
+ new Set(quads.map(q => q.sliceIndex)).size !== groups.length)
+ throw new TypeError('Volume layer axis counts differ from the retained plan.');
+ const close = (a: number, b: number) => Number.isFinite(a) && Number.isFinite(b) &&
+ Math.abs(a - b) <= 1e-10 * Math.max(1, Math.abs(min), Math.abs(max));
+ for (const q of quads) {
+ const group = groups[q.sliceIndex], slab = readVolumeSlabInterval(q.slab);
+ if (!group || slab.startCell !== group.startCell || slab.endCell !== group.endCell ||
+ slab.samples !== (group.endCell - group.startCell) * plan.referenceSamplesPerSlab ||
+ !close(slab.start, min + group.startCell * pitch) || !close(slab.end, min + group.endCell * pitch) ||
+ !close(q.center[axial]!, (slab.start + slab.end) / 2) ||
+ q.vertices.some(p => !close(p[axial]!, q.center[axial]!)))
+ throw new TypeError('Physical slab interval, plane or samples differ from the retained volume layer plan.');
+ }
+ }
+ return plan;
}
diff --git a/labs/nebula/packages/volume-core/src/coordinates/compiler-frame-layers.test.ts b/labs/nebula/packages/volume-core/src/coordinates/compiler-frame-layers.test.ts
new file mode 100644
index 0000000000..d20befc687
--- /dev/null
+++ b/labs/nebula/packages/volume-core/src/coordinates/compiler-frame-layers.test.ts
@@ -0,0 +1,47 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { compilerPreparedSlices } from './compiler-frame.ts';
+import { validateVolumeLayerSlices, type VolumeLayerPlan, type VolumeSlices, type VolumeSliceQuad } from '../contracts/volume-slices.ts';
+import type { Vector3 } from '../contracts/volume-recipe.ts';
+
+function sourceSlices(): VolumeSlices {
+ const boundsUnits = { min: [-2, -3, -5] as Vector3, max: [2, 5, 11] as Vector3 };
+ const plan: VolumeLayerPlan = { schema: 'cssearth-volume-layer-plan@1', referenceSliceCounts: { x: 4, y: 4, z: 8 }, referenceSamplesPerSlab: 4,
+ axes: { x: [{ startCell: 0, endCell: 1 }, { startCell: 1, endCell: 4 }],
+ y: [{ startCell: 0, endCell: 3 }, { startCell: 3, endCell: 4 }],
+ z: [{ startCell: 0, endCell: 1 }, { startCell: 1, endCell: 3 }, { startCell: 3, endCell: 8 }] } };
+ const quads: VolumeSliceQuad[] = [];
+ for (const [axial, axis] of (['x', 'y', 'z'] as const).entries()) for (const [sliceIndex, group] of plan.axes[axis].entries()) {
+ const pitch = (boundsUnits.max[axial]! - boundsUnits.min[axial]!) / plan.referenceSliceCounts[axis];
+ const start = boundsUnits.min[axial]! + group.startCell * pitch, end = boundsUnits.min[axial]! + group.endCell * pitch, depth = (start + end) / 2;
+ const point = (u: number, v: number): Vector3 => axis === 'x' ? [depth, u, v] : axis === 'y' ? [u, depth, v] : [u, v, depth];
+ const horizontal = axis === 'x' ? 1 : 0, vertical = axis === 'z' ? 1 : 2;
+ const u0 = boundsUnits.min[horizontal], u1 = boundsUnits.max[horizontal], v0 = boundsUnits.min[vertical], v1 = boundsUnits.max[vertical];
+ quads.push({ id: `${axis}-${sliceIndex}`, axis, sliceIndex, texturePath: `${axis}-${sliceIndex}.png`, widthPx: 2, heightPx: 2,
+ vertices: [point(u0, v1), point(u1, v1), point(u1, v0), point(u0, v0)], center: point((u0 + u1) / 2, (v0 + v1) / 2),
+ normal: axis === 'x' ? [1, 0, 0] : axis === 'y' ? [0, 1, 0] : [0, 0, 1], uvs: [[0, 0], [1, 0], [1, 1], [0, 1]],
+ sha256: 'a'.repeat(64), bytes: 1, alphaCoverage: 1,
+ slab: { start, end, startCell: group.startCell, endCell: group.endCell, samples: (group.endCell - group.startCell) * 4 } });
+ }
+ return { quads, boundsUnits, provenance: {}, approximation: { method: 'fixture', radialEmission: 'none', limitations: [],
+ samplesPerSlab: 4, opticalWeight: 1, exposureGain: 1, sliceCounts: { x: 2, y: 2, z: 3 },
+ slabPitchUnits: { x: 2, y: 4, z: 16 / 3 }, layerPlan: plan } };
+}
+
+test('prepared reflection keeps planned intervals, sample counts and logical indices valid in the reflected frame', () => {
+ const source = sourceSlices(), before = structuredClone(source);
+ assert.ok(validateVolumeLayerSlices(source));
+ const prepared = compilerPreparedSlices(source);
+ assert.ok(validateVolumeLayerSlices(prepared), 'Prepared interval metadata must describe the actual reflected physical planes.');
+ assert.deepEqual(source, before);
+ assert.deepEqual(prepared.boundsUnits, { min: [-2, -3, -11], max: [2, 5, 5] });
+ assert.deepEqual(prepared.approximation.layerPlan!.axes.z,
+ [{ startCell: 0, endCell: 5 }, { startCell: 5, endCell: 7 }, { startCell: 7, endCell: 8 }]);
+ assert.deepEqual(prepared.quads.filter(q => q.axis === 'z').map(q => ({ id: q.id, index: q.sliceIndex, slab: q.slab })), [
+ { id: 'z-0', index: 2, slab: { start: 3, end: 5, samples: 4, startCell: 7, endCell: 8 } },
+ { id: 'z-1', index: 1, slab: { start: -1, end: 3, samples: 8, startCell: 5, endCell: 7 } },
+ { id: 'z-2', index: 0, slab: { start: -11, end: -1, samples: 20, startCell: 0, endCell: 5 } },
+ ]);
+ assert.deepEqual(prepared.quads.map(q => [q.id, q.texturePath, q.sha256, q.uvs]), source.quads.map(q => [q.id, q.texturePath, q.sha256, q.uvs]));
+ assert.deepEqual(compilerPreparedSlices(prepared), source, 'Reflecting geometry and interval metadata twice is an exact identity.');
+});
diff --git a/labs/nebula/packages/volume-core/src/coordinates/compiler-frame.ts b/labs/nebula/packages/volume-core/src/coordinates/compiler-frame.ts
index 1ec25a4fae..36c838519a 100644
--- a/labs/nebula/packages/volume-core/src/coordinates/compiler-frame.ts
+++ b/labs/nebula/packages/volume-core/src/coordinates/compiler-frame.ts
@@ -2,18 +2,36 @@ import type { DensityVolumeFrame } from '../contracts/volume-frame.ts';
import type { Bounds3, Vector3 } from '../contracts/volume-recipe.ts';
import type { EmissionBounds, EmissionVector3 } from '../contracts/emission.ts';
import { COMPILER_LONGEST_AXIS_SLICES } from '../contracts/compiler-bake.ts';
+import { validateVolumeLayerSlices, type VolumeSlices } from '../contracts/volume-slices.ts';
+
+/** Angular source axes west/north/away are left-handed. Prepared physical axes are west/north/toward. */
+export const COMPILER_PHYSICAL_REFERENCE = 'lab-sky-west-north-toward';
+export const compilerPreparedPoint = (point: readonly number[]): Vector3 => [point[0]!, point[1]!, -point[2]!];
+export function compilerPreparedSlices(source: VolumeSlices): VolumeSlices {
+ const plan = validateVolumeLayerSlices(source);
+ const reflectGroup = (group: { startCell: number; endCell: number }) => ({
+ startCell: plan!.referenceSliceCounts.z - group.endCell, endCell: plan!.referenceSliceCounts.z - group.startCell });
+ return { ...source, boundsUnits: { min: [source.boundsUnits.min[0], source.boundsUnits.min[1], -source.boundsUnits.max[2]],
+ max: [source.boundsUnits.max[0], source.boundsUnits.max[1], -source.boundsUnits.min[2]] },
+ ...(plan ? { approximation: { ...source.approximation, layerPlan: { ...plan,
+ axes: { ...plan.axes, z: [...plan.axes.z].reverse().map(reflectGroup) } } } } : {}),
+ quads: source.quads.map(quad => ({ ...quad, center: compilerPreparedPoint(quad.center),
+ ...(plan && quad.axis === 'z' ? { sliceIndex: plan.axes.z.length - 1 - quad.sliceIndex,
+ slab: { ...quad.slab!, ...reflectGroup(quad.slab!), start: -quad.slab!.end, end: -quad.slab!.start } } : {}),
+ normal: compilerPreparedPoint(quad.normal), vertices: quad.vertices.map(compilerPreparedPoint) as typeof quad.vertices })) };
+}
export function validCompilerBounds(bounds: EmissionBounds): boolean {
return Array.isArray(bounds?.min) && Array.isArray(bounds?.max) && bounds.min.length === 3 && bounds.max.length === 3 &&
bounds.min.every((n, i) => Number.isFinite(n) && Number.isFinite(bounds.max[i]) && n < bounds.max[i]!);
}
-export function compilerFrame(bounds: EmissionBounds): { origin: EmissionVector3; localBounds: Bounds3; frame: DensityVolumeFrame } {
+export function compilerFrame(bounds: EmissionBounds, preparedPhysical = false): { origin: EmissionVector3; localBounds: Bounds3; frame: DensityVolumeFrame } {
if (!validCompilerBounds(bounds)) throw new TypeError('Compiler frame requires finite increasing west/north/away bounds.');
const origin = bounds.min.map((n, i) => (n + bounds.max[i]!) / 2) as EmissionVector3;
const localBounds: Bounds3 = { min: bounds.min.map((n, i) => n - origin[i]!) as Vector3,
max: bounds.max.map((n, i) => n - origin[i]!) as Vector3 };
- return { origin, localBounds, frame: { referenceFrame: 'lab-sky-angular', epochJdTt: 2451545,
+ return { origin, localBounds, frame: { referenceFrame: preparedPhysical ? COMPILER_PHYSICAL_REFERENCE : 'lab-sky-angular', epochJdTt: 2451545,
originM: [0, 0, 0], localToReferenceXyzw: [0, 0, 0, 1], metersPerUnit: 1, boundsUnits: localBounds } };
}
diff --git a/labs/nebula/packages/volume-core/src/fields/photometric-emission.ts b/labs/nebula/packages/volume-core/src/fields/photometric-emission.ts
new file mode 100644
index 0000000000..9e093cf688
--- /dev/null
+++ b/labs/nebula/packages/volume-core/src/fields/photometric-emission.ts
@@ -0,0 +1,75 @@
+/** Retained two-scale light: an explicit smooth density plus finite 3D residual features. */
+import type { EmissionFieldModel, EmissionVector3, SkyBounds } from '../contracts/emission.ts';
+import { createEmissionField } from './emission.ts';
+import { readPhotometricMgeRecipe, samplePhotometricMge, type PhotometricMgeRecipe } from './photometric-mge.ts';
+import { createEnvelopeSampler, sampleEnvelopeGrid } from './simulation-envelope.ts';
+
+export interface RetainedPhotometricEnvelope {
+ schema: 'cssearth-photometric-envelope@1'; priorIdentity: string; recipe: PhotometricMgeRecipe;
+ width: number; height: number; bounds: SkyBounds; zRange: [number, number]; gain: number[];
+}
+const record = (v: unknown): v is Record => v !== null && typeof v === 'object' && !Array.isArray(v);
+const pair = (v: unknown): v is [number, number] => Array.isArray(v) && v.length === 2 && v.every(Number.isFinite);
+export function readPhotometricEnvelope(v: unknown): RetainedPhotometricEnvelope {
+ if (!record(v) || v.schema !== 'cssearth-photometric-envelope@1' || typeof v.priorIdentity !== 'string' || !/^[a-f0-9]{64}$/.test(v.priorIdentity) ||
+ typeof v.width !== 'number' || !Number.isInteger(v.width) || v.width < 2 || v.width > 2048 ||
+ typeof v.height !== 'number' || !Number.isInteger(v.height) || v.height < 2 || v.height > 2048 || v.width * v.height > 2_000_000 ||
+ !record(v.bounds) || !pair(v.bounds.min) || !pair(v.bounds.max) ||
+ !pair(v.zRange) || v.zRange[0] >= v.zRange[1] || !Array.isArray(v.gain) || v.gain.length !== v.width * v.height ||
+ !v.gain.every((n): n is number => typeof n === 'number' && Number.isFinite(n) && n >= 0)) throw new TypeError('Invalid retained photometric envelope.');
+ const maximum = v.bounds.max;
+ if (v.bounds.min.some((n, i) => n >= maximum[i]!)) throw new TypeError('Invalid retained envelope bounds.');
+ const recipe = readPhotometricMgeRecipe(v.recipe);
+ if (!recipe.envelope) throw new TypeError('Retained envelope requires explicit recipe settings.');
+ return { schema: v.schema, priorIdentity: v.priorIdentity, recipe, width: v.width, height: v.height,
+ bounds: { min: v.bounds.min, max: v.bounds.max }, zRange: v.zRange, gain: v.gain };
+}
+export interface EnvelopeColors { width: number; height: number; rgb: number[] }
+export function readEnvelopeColors(v: unknown, envelope: RetainedPhotometricEnvelope): EnvelopeColors {
+ if (!record(v) || typeof v.width !== 'number' || !Number.isInteger(v.width) || v.width < 2 || v.width > envelope.width ||
+ typeof v.height !== 'number' || !Number.isInteger(v.height) || v.height < 2 || v.height > envelope.height ||
+ !Array.isArray(v.rgb) || v.rgb.length !== v.width * v.height * 3 || !v.rgb.every((n): n is number => typeof n === 'number' && Number.isFinite(n) && n >= 0 && n <= 255))
+ throw new TypeError('Missing or invalid retained envelope colors.');
+ return { width: v.width, height: v.height, rgb: v.rgb };
+}
+/** Research and compact replay consume exactly the same retained numbers and arithmetic. */
+export function createPhotometricEmission(model: EmissionFieldModel) {
+ const finite = createEmissionField(model), retained = model.photometricEnvelope;
+ const envelope = retained && readPhotometricEnvelope(retained);
+ const sampleEnvelope = envelope && createEnvelopeSampler({ ...envelope, gain: Float32Array.from(envelope.gain) },
+ { identity: envelope.priorIdentity, ...samplePhotometricMge(envelope.recipe) });
+ const bounds = envelope ? { min: [...finite.bounds.min] as EmissionVector3, max: [...finite.bounds.max] as EmissionVector3 } : finite.bounds;
+ if (envelope) for (let axis = 0; axis < 3; axis++) {
+ bounds.min[axis] = Math.min(bounds.min[axis]!, axis === 2 ? envelope.zRange[0] : envelope.bounds.min[axis]!);
+ bounds.max[axis] = Math.max(bounds.max[axis]!, axis === 2 ? envelope.zRange[1] : envelope.bounds.max[axis]!);
+ }
+ const sampleEmission: typeof finite.sampleEmission = (x, y, z, out) => {
+ finite.sampleEmission(x, y, z, out);
+ const extra = sampleEnvelope?.(x, y, z) ?? 0;
+ out[0] = out[1] = out[2] = out[0]! + extra;
+ };
+ const createMaterialSampler = (colors: Parameters[0], envelopeColors?: EnvelopeColors) => {
+ const sampleFinite = finite.createMaterialSampler(colors);
+ if (!envelope || !sampleEnvelope) {
+ if (envelopeColors !== undefined) throw new TypeError('Envelope colors have no retained envelope.');
+ return sampleFinite;
+ }
+ const coarse = readEnvelopeColors(envelopeColors, envelope), coarseGrid = { ...envelope, width: coarse.width, height: coarse.height }, light: EmissionVector3 = [0, 0, 0], rgb: EmissionVector3 = [0, 0, 0];
+ return (x: number, y: number, z: number, out: EmissionVector3): boolean => {
+ finite.sampleEmission(x, y, z, light);
+ const componentLight = light[0], envelopeLight = sampleEnvelope(x, y, z), total = componentLight + envelopeLight;
+ if (!(total > 0)) return false;
+ if (!sampleFinite(x, y, z, out)) out[0] = out[1] = out[2] = 255;
+ if (!sampleEnvelopeGrid(coarseGrid, coarse.rgb, 3, x, y, rgb)) rgb[0] = rgb[1] = rgb[2] = 255;
+ for (let c = 0; c < 3; c++) {
+ const mixed = (componentLight * out[c]! + envelopeLight * rgb[c]!) / total;
+ if (!Number.isFinite(mixed)) throw new TypeError('Photometric material mixture must remain finite.');
+ // Validated colors and nonnegative light form a convex mixture. Bound its floating-point
+ // roundoff at the channel endpoints (a real saturated sample produced 255.00000000000003).
+ out[c] = Math.min(255, Math.max(0, mixed));
+ }
+ return true;
+ };
+ };
+ return { finite, bounds, sampleEmission, createMaterialSampler };
+}
diff --git a/labs/nebula/packages/volume-core/src/fields/photometric-mge.ts b/labs/nebula/packages/volume-core/src/fields/photometric-mge.ts
new file mode 100644
index 0000000000..371bed13a9
--- /dev/null
+++ b/labs/nebula/packages/volume-core/src/fields/photometric-mge.ts
@@ -0,0 +1,93 @@
+/** Published projected Gaussian light profiles, deprojected under an explicit oblate hypothesis. */
+const jointRecord = (v: unknown): v is Record => v !== null && typeof v === 'object' && !Array.isArray(v);
+const jointPath = (v: unknown): v is string => typeof v === 'string' && /^[a-zA-Z0-9._/-]+$/.test(v) && !v.startsWith('/') && !v.split('/').includes('..');
+import type { SimulationDepthPrior } from '../contracts/simulation-prior.ts';
+import { validateEnvelopeSettings, type SimulationEnvelopeSettings } from './simulation-envelope.ts';
+
+export interface PhotometricGaussian {
+ centralAmplitude: number;
+ sigmaArcsec: number;
+ projectedAxisRatio: number;
+}
+export interface PhotometricMgeRecipe {
+ schema: 'cssearth-photometric-mge@1'; id: string;
+ centerIcrsDegrees: [number, number]; distancePc: number;
+ positionAngleEastOfNorthDegrees: number; inclinationDegrees: number;
+ /** The photometric projection cannot choose which pole tilts away from the observer. */
+ lineOfSightTiltSign: 1 | -1;
+ cutoffSigma: number;
+ gaussians: PhotometricGaussian[];
+ evidence: { path: string; sha256: string };
+ source: { url: string; sha256: string; locator: string };
+ interpretation: string;
+ envelope?: SimulationEnvelopeSettings;
+ /** Authored finite residual fit budget; omitted retains the historical compiler control. */
+ residualMaximumComponents?: number;
+}
+const finite = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value);
+const between = (value: unknown, low: number, high: number): value is number => finite(value) && value >= low && value <= high;
+
+export function readPhotometricMgeRecipe(value: unknown): PhotometricMgeRecipe {
+ if (!jointRecord(value) || value.schema !== 'cssearth-photometric-mge@1' ||
+ typeof value.id !== 'string' || !/^[a-z0-9][a-z0-9-]{0,95}$/.test(value.id) ||
+ !Array.isArray(value.centerIcrsDegrees) || value.centerIcrsDegrees.length !== 2 ||
+ !between(value.centerIcrsDegrees[0], 0, 359.999999999) || !between(value.centerIcrsDegrees[1], -90, 90) ||
+ !between(value.distancePc, .01, 1e9) || !between(value.positionAngleEastOfNorthDegrees, 0, 180) ||
+ !between(value.inclinationDegrees, .01, 90) || ![1, -1].includes(Number(value.lineOfSightTiltSign)) ||
+ typeof value.lineOfSightTiltSign !== 'number' || !between(value.cutoffSigma, 4, 8) ||
+ !Array.isArray(value.gaussians) || value.gaussians.length < 1 || value.gaussians.length > 128 ||
+ !jointRecord(value.evidence) || !jointPath(value.evidence.path) ||
+ typeof value.evidence.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(value.evidence.sha256) ||
+ !jointRecord(value.source) || typeof value.source.url !== 'string' || !value.source.url.startsWith('https://') ||
+ typeof value.source.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(value.source.sha256) ||
+ typeof value.source.locator !== 'string' || !value.source.locator.trim() ||
+ typeof value.interpretation !== 'string' || !value.interpretation.trim())
+ throw new TypeError('Invalid pinned photometric MGE recipe.');
+ if (value.residualMaximumComponents !== undefined && (!between(value.residualMaximumComponents, 16, 8192) || !Number.isInteger(value.residualMaximumComponents)))
+ throw new TypeError('Invalid photometric residual component budget.');
+ const cosSquared = Math.cos(value.inclinationDegrees * Math.PI / 180) ** 2;
+ const gaussians = value.gaussians.map((row: unknown): PhotometricGaussian => {
+ if (!jointRecord(row) || !between(row.centralAmplitude, 1e-12, 1e20) ||
+ !between(row.sigmaArcsec, .001, 1e6) || !between(row.projectedAxisRatio, .001, 1) ||
+ row.projectedAxisRatio ** 2 <= cosSquared)
+ throw new TypeError('Photometric Gaussian cannot be deprojected at this inclination.');
+ return { centralAmplitude: row.centralAmplitude, sigmaArcsec: row.sigmaArcsec, projectedAxisRatio: row.projectedAxisRatio };
+ });
+ return { schema: value.schema, id: value.id, centerIcrsDegrees: [value.centerIcrsDegrees[0], value.centerIcrsDegrees[1]],
+ distancePc: value.distancePc, positionAngleEastOfNorthDegrees: value.positionAngleEastOfNorthDegrees,
+ inclinationDegrees: value.inclinationDegrees, lineOfSightTiltSign: value.lineOfSightTiltSign === 1 ? 1 : -1,
+ cutoffSigma: value.cutoffSigma, gaussians, evidence: { path: value.evidence.path, sha256: value.evidence.sha256 },
+ source: { url: value.source.url, sha256: value.source.sha256, locator: value.source.locator }, interpretation: value.interpretation,
+ ...(value.envelope === undefined ? {} : { envelope: validateEnvelopeSettings(value.envelope) }),
+ ...(value.residualMaximumComponents === undefined ? {} : { residualMaximumComponents: value.residualMaximumComponents }) };
+}
+
+/** x is west, y north, z away. The density is relative light per angular-depth unit, not calibrated flux. */
+export function samplePhotometricMge(recipe: PhotometricMgeRecipe): Omit {
+ const parsed = readPhotometricMgeRecipe(recipe);
+ const inclination = parsed.inclinationDegrees * Math.PI / 180, pa = parsed.positionAngleEastOfNorthDegrees * Math.PI / 180;
+ const sinI = Math.sin(inclination), cosI = Math.cos(inclination), sign = parsed.lineOfSightTiltSign;
+ const axis = [sinI * Math.cos(pa), sinI * Math.sin(pa), sign * cosI];
+ const totalAmplitude = parsed.gaussians.reduce((sum, row) => sum + row.centralAmplitude, 0);
+ const rows = parsed.gaussians.map(row => {
+ const qSquared = (row.projectedAxisRatio ** 2 - cosI ** 2) / sinI ** 2;
+ return { inverseVariance: 1 / row.sigmaArcsec ** 2, inverseQSquared: 1 / qSquared,
+ amplitude: row.centralAmplitude / totalAmplitude * row.projectedAxisRatio /
+ (Math.sqrt(2 * Math.PI) * row.sigmaArcsec * Math.sqrt(qSquared)) };
+ });
+ // An enclosing sphere retains every tilted Gaussian out to the explicitly chosen ellipsoidal cutoff.
+ const radius = parsed.cutoffSigma * Math.max(...parsed.gaussians.map(row => row.sigmaArcsec));
+ const cutoffSquared = parsed.cutoffSigma ** 2;
+ return { bounds: { min: [-radius, -radius, -radius], max: [radius, radius, radius] },
+ sampleDensity(x, y, z) {
+ if (![x, y, z].every(Number.isFinite)) throw new TypeError('MGE coordinates must be finite.');
+ const axial = axis[0]! * x + axis[1]! * y + axis[2]! * z;
+ const cylindricalSquared = Math.max(0, x * x + y * y + z * z - axial * axial);
+ let density = 0;
+ for (const row of rows) {
+ const radiusSquared = (cylindricalSquared + axial * axial * row.inverseQSquared) * row.inverseVariance;
+ if (radiusSquared < cutoffSquared) density += row.amplitude * Math.exp(-.5 * radiusSquared);
+ }
+ return density;
+ } };
+}
diff --git a/labs/nebula/packages/volume-core/src/fields/retained-emission.ts b/labs/nebula/packages/volume-core/src/fields/retained-emission.ts
index 113ea5f421..ec7ec15fc5 100644
--- a/labs/nebula/packages/volume-core/src/fields/retained-emission.ts
+++ b/labs/nebula/packages/volume-core/src/fields/retained-emission.ts
@@ -1,12 +1,13 @@
const jointRecord = (v: unknown): v is Record => v !== null && typeof v === 'object' && !Array.isArray(v);
import { readCompilerControls } from '../contracts/compiler-controls.ts';
import type { EmissionComponent, EmissionFieldModel } from '../contracts/emission.ts';
+import { readPhotometricEnvelope } from './photometric-emission.ts';
import { readEmissionWindow } from './emission-window.ts';
const triple = (v: unknown): v is [number, number, number] => Array.isArray(v) && v.length === 3 && v.every(Number.isFinite);
function component(v: unknown): v is EmissionComponent {
return jointRecord(v) && typeof v.id === 'string' && typeof v.basisId === 'string' && triple(v.center) && triple(v.sigma) && v.sigma.every(n => n > 0) &&
typeof v.angleRadians === 'number' && Number.isFinite(v.angleRadians) && typeof v.projectedWeight === 'number' && Number.isFinite(v.projectedWeight) && v.projectedWeight >= 0 &&
- ['scaffold-near', 'scaffold-far', 'halo-near', 'halo-far', 'halo-diffuse', 'evidence-surface'].includes(String(v.depthAssignment)) && typeof v.velocityCovered === 'boolean' &&
+ ['scaffold-near', 'scaffold-far', 'halo-near', 'halo-far', 'halo-diffuse', 'evidence-surface', 'simulation-prior', 'unsupported-local'].includes(String(v.depthAssignment)) && typeof v.velocityCovered === 'boolean' &&
(v.depthGradient === undefined || Array.isArray(v.depthGradient) && v.depthGradient.length === 2 && v.depthGradient.every(n => Number.isFinite(n) && Math.abs(n) <= 100));
}
/** Decode the spatial field consumed by independent stellar/material preparation. Original provenance stays immutable. */
@@ -18,6 +19,7 @@ export function readRetainedEmissionField(v: unknown): EmissionFieldModel {
if (v.bounds.min.some((n, i) => n >= maximum[i]!)) throw new TypeError('Invalid retained emission bounds.');
return { schema: v.schema, identity: v.identity, controls: readCompilerControls(v.controls), bounds: { min: v.bounds.min, max: v.bounds.max },
...(v.emissionWindow === undefined ? {} : { emissionWindow: readEmissionWindow(v.emissionWindow) }),
+ ...(v.photometricEnvelope === undefined ? {} : { photometricEnvelope: readPhotometricEnvelope(v.photometricEnvelope) }),
components: v.components, skyBounds: { min: [v.bounds.min[0], v.bounds.min[1]], max: [v.bounds.max[0], v.bounds.max[1]] }, scaffold: null,
assumptions: { kernel: 'retained source components', projectionUnits: 'arcseconds', depth: 'retained authored field', halo: 'retained source',
haloRadiusArcsec: 1, equalNearFarSplit: true, velocityUncoveredComponents: 0 } };
diff --git a/labs/nebula/packages/volume-core/src/fields/simulation-envelope.ts b/labs/nebula/packages/volume-core/src/fields/simulation-envelope.ts
index 59b3e5a590..e84982b56a 100644
--- a/labs/nebula/packages/volume-core/src/fields/simulation-envelope.ts
+++ b/labs/nebula/packages/volume-core/src/fields/simulation-envelope.ts
@@ -119,7 +119,7 @@ export const pixelCenter = (bounds: SkyBounds, width: number, height: number, p:
];
/** Bilinear lookup in a row-major sky grid; false outside the grid. */
-function sampleGrid(grid: { width: number; height: number; bounds: SkyBounds }, values: ArrayLike, channels: number, x: number, y: number, out: number[]): boolean {
+export function sampleEnvelopeGrid(grid: { width: number; height: number; bounds: SkyBounds }, values: ArrayLike, channels: number, x: number, y: number, out: number[]): boolean {
const u = (x - grid.bounds.min[0]) / (grid.bounds.max[0] - grid.bounds.min[0]) * grid.width - .5;
const v = (grid.bounds.max[1] - y) / (grid.bounds.max[1] - grid.bounds.min[1]) * grid.height - .5;
if (!(u >= -.5 && v >= -.5 && u <= grid.width - .5 && v <= grid.height - .5)) return false;
@@ -135,7 +135,7 @@ function sampleGrid(grid: { width: number; height: number; bounds: SkyBounds },
export function createEnvelopeSampler(grid: SimulationEnvelopeGrid, prior: SimulationDepthPrior) {
const scratch = [0];
return (x: number, y: number, z: number): number => {
- if (z < grid.zRange[0] || z > grid.zRange[1] || !sampleGrid(grid, grid.gain, 1, x, y, scratch) || !(scratch[0]! > 0)) return 0;
+ if (z < grid.zRange[0] || z > grid.zRange[1] || !sampleEnvelopeGrid(grid, grid.gain, 1, x, y, scratch) || !(scratch[0]! > 0)) return 0;
return scratch[0]! * prior.sampleDensity(x, y, z);
};
}
@@ -188,7 +188,7 @@ export function envelopeChromaticity(rgb: ArrayLike, coverage: Uint8Arra
}
const grid = { width, height, bounds }, scratch = [0, 0, 0];
return (x: number, y: number, out: [number, number, number]): boolean => {
- if (!sampleGrid(grid, chroma, 3, x, y, scratch)) return false;
+ if (!sampleEnvelopeGrid(grid, chroma, 3, x, y, scratch)) return false;
for (let c = 0; c < 3; c++) out[c] = Math.min(255, Math.max(0, scratch[c]!));
return true;
};
diff --git a/labs/nebula/packages/volume-core/src/materials/material.test.ts b/labs/nebula/packages/volume-core/src/materials/material.test.ts
index cc8da33bc3..75c213a3e8 100644
--- a/labs/nebula/packages/volume-core/src/materials/material.test.ts
+++ b/labs/nebula/packages/volume-core/src/materials/material.test.ts
@@ -2,6 +2,20 @@ import test from 'node:test';
import assert from 'node:assert/strict';
import { compilerSlabMaterial, alphaLimitedSlabMaterial, channelGainSlabMaterial, lensChannelGainMaterial, splitChannelGain, validateChannelGain } from '@cssearth/volume-core/materials/slab-material';
+test('explicit slab offsets and quadrature spacing govern chroma and alpha-limited color', () => {
+ const emission = (x: number, _y: number, _z: number, out: [number, number, number]) => out.fill(x < 0 ? .01 : .03);
+ const material = (_x: number, _y: number, _z: number, out: [number, number, number]) => { out[0] = 255; out[1] = out[2] = 0; return true; };
+ const visits: number[] = [], sample = compilerSlabMaterial(emission, (x, y, z, out) => { visits.push(x); return material(x, y, z, out); });
+ const out: [number, number, number] = [0, 0, 0];
+ const slab = { axis: 'x' as const, pitch: 100, samples: 2, sampleOffsets: [-.75, .25], sampleSpacing: .5 };
+ assert.ok(alphaLimitedSlabMaterial(sample, emission, 1, 24)(0, 0, 0, out, slab));
+ assert.deepEqual(visits, [-.75, .25]);
+ const trust = 255 * -Math.expm1(-.02) / 24;
+ assert.ok(Math.abs(out[1] - 255 * (1 - trust)) < 1e-10, 'Alpha uses the reference step, never mean pitch.');
+ assert.throws(() => sample(0, 0, 0, out, { ...slab, sampleOffsets: [0] }), /matching offsets/);
+ assert.throws(() => sample(0, 0, 0, out, { ...slab, sampleOffsets: [NaN, 0] }), /finite/);
+});
+
test('thin displaced emission takes its own image color rather than the empty slice center', () => {
const color: [number, number, number] = [0, 0, 0];
const sample = compilerSlabMaterial((x, _y, _z, out) => { out[0] = out[1] = out[2] = x > .5 ? 2 : 0; },
diff --git a/labs/nebula/packages/volume-core/src/materials/slab-material.ts b/labs/nebula/packages/volume-core/src/materials/slab-material.ts
index e97da155ca..5026fa6738 100644
--- a/labs/nebula/packages/volume-core/src/materials/slab-material.ts
+++ b/labs/nebula/packages/volume-core/src/materials/slab-material.ts
@@ -2,14 +2,34 @@ type Vector3 = [number, number, number];
type Sample = (x: number, y: number, z: number, out: Vector3) => void;
type MaterialSample = (x: number, y: number, z: number, out: Vector3) => boolean;
+export interface SlabMaterialSampling {
+ axis: 'x' | 'y' | 'z'; pitch: number; samples: number;
+ /** Reference-grid sample positions relative to the quad plane, in the caller's physical units. */
+ sampleOffsets?: readonly number[];
+ /** Reference quadrature step; required with sampleOffsets. */
+ sampleSpacing?: number;
+}
+function validateOffsets(slab: SlabMaterialSampling) {
+ if (slab.sampleOffsets === undefined && slab.sampleSpacing === undefined) return;
+ if (!slab.sampleOffsets || slab.sampleOffsets.length !== slab.samples || slab.samples < 1 || slab.samples > 4096 ||
+ !Number.isFinite(slab.sampleSpacing) || !(slab.sampleSpacing! > 0))
+ throw new TypeError('Explicit slab samples require matching offsets and a finite positive quadrature step.');
+}
+const sampleOffset = (slab: SlabMaterialSampling, i: number) => {
+ if (!slab.sampleOffsets) return slab.pitch * ((i + .5) / slab.samples - .5);
+ const offset = slab.sampleOffsets[i]!;
+ if (!Number.isFinite(offset)) throw new TypeError('Slab sample offsets must be finite.');
+ return offset;
+};
/** Color follows the same emitting sub-samples as neutral alpha, not an empty slab midpoint. */
export function compilerSlabMaterial(sampleEmission: Sample, sampleMaterial: MaterialSample) {
const emission: Vector3 = [0, 0, 0], color: Vector3 = [0, 0, 0];
- return (x: number, y: number, z: number, out: Vector3, slab: { axis: 'x' | 'y' | 'z'; pitch: number; samples: number }): boolean => {
+ return (x: number, y: number, z: number, out: Vector3, slab: SlabMaterialSampling): boolean => {
+ validateOffsets(slab);
let weight = 0, covered = 0;
out[0] = out[1] = out[2] = 0;
for (let i = 0; i < slab.samples; i++) {
- const offset = slab.pitch * ((i + .5) / slab.samples - .5);
+ const offset = sampleOffset(slab, i);
const sx = x + (slab.axis === 'x' ? offset : 0), sy = y + (slab.axis === 'y' ? offset : 0), sz = z + (slab.axis === 'z' ? offset : 0);
sampleEmission(sx, sy, sz, emission); const light = emission[0];
if (!(light > 0)) continue;
@@ -40,12 +60,14 @@ export function alphaLimitedSlabMaterial(material: SlabMaterial, sampleEmission:
throw new TypeError('Alpha-limited material requires positive exposure and a full-chroma alpha byte in 1..255.');
const emission: Vector3 = [0, 0, 0];
return (x, y, z, out, slab) => {
+ validateOffsets(slab);
if (!material(x, y, z, out, slab)) return false;
let integrated = 0;
for (let i = 0; i < slab.samples; i++) {
- const offset = slab.pitch * ((i + .5) / slab.samples - .5);
+ const offset = sampleOffset(slab, i);
sampleEmission(x + (slab.axis === 'x' ? offset : 0), y + (slab.axis === 'y' ? offset : 0), z + (slab.axis === 'z' ? offset : 0), emission);
- integrated += Math.max(0, emission[0]) * slab.pitch / slab.samples;
+ integrated += slab.sampleSpacing === undefined ? Math.max(0, emission[0]) * slab.pitch / slab.samples
+ : Math.max(0, emission[0]) * slab.sampleSpacing;
}
const trust = Math.min(1, 255 * -Math.expm1(-exposureGain * integrated) / fullChromaAlphaByte);
for (let c = 0; c < 3; c++) out[c] = Math.min(255, Math.max(0, 255 - (255 - out[c]!) * trust));
diff --git a/labs/nebula/packages/volume-core/src/sampling/layer-optimization.test.ts b/labs/nebula/packages/volume-core/src/sampling/layer-optimization.test.ts
new file mode 100644
index 0000000000..2f79884985
--- /dev/null
+++ b/labs/nebula/packages/volume-core/src/sampling/layer-optimization.test.ts
@@ -0,0 +1,90 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { optimizeVolumeLayers, readLayerOptimizationReport, type LayerOptimizationOptions } from './layer-optimization.ts';
+import type { VolumeLayerPlan } from '../contracts/volume-slices.ts';
+
+const axes = ['x', 'y', 'z'] as const;
+const uniform = (): LayerOptimizationOptions => ({ bounds: { min: [-1, -1, -1], max: [1, 1, 1] },
+ referenceSliceCounts: { x: 512, y: 512, z: 512 }, referenceSamplesPerSlab: 1, probeWidth: 8,
+ targetNormalizedL1: 1e-12, sampleEmission(_x, _y, _z, out) { out.fill(1); } });
+function complete(plan: VolumeLayerPlan) {
+ for (const axis of axes) assert.deepEqual(plan.axes[axis].flatMap(group =>
+ Array.from({ length: group.endCell - group.startCell }, (_, i) => group.startCell + i)),
+ Array.from({ length: plan.referenceSliceCounts[axis] }, (_, i) => i), 'Every full-depth reference cell occurs exactly once.');
+}
+
+test('default planner deterministically caps the combined XYZ layers at 500 without dropping cells', () => {
+ const options = uniform(), first = optimizeVolumeLayers(options), again = optimizeVolumeLayers(options);
+ assert.deepEqual(first, again);
+ assert.equal(axes.reduce((n, axis) => n + first.plan.axes[axis].length, 0), 500);
+ assert.equal(first.report.maximumLayers, 500);
+ assert.equal(first.report.referenceLayers, 1536);
+ assert.equal(first.report.plannedLayers, 500);
+ assert.equal(first.report.status, 'budget-limited', 'A layer budget does not certify the unmet error target.');
+ complete(first.plan);
+ assert.deepEqual(readLayerOptimizationReport(first.report, first.plan), first.report);
+ assert.throws(() => readLayerOptimizationReport({ ...first.report, status: 'target-met' }, first.plan), /differs from/);
+});
+
+test('an off-center concentrated feature receives finer groups than faint outer support', () => {
+ const result = optimizeVolumeLayers({ ...uniform(), referenceSliceCounts: { x: 64, y: 64, z: 64 }, maximumLayers: 24,
+ referenceSamplesPerSlab: 4, probeWidth: 16, sampleEmission(x, y, z, out) {
+ out.fill(.001 + 20 * Math.exp(-.5 * (((x - .625) / .07) ** 2 + ((y + .375) / .18) ** 2 + ((z - .3125) / .09) ** 2)));
+ } });
+ complete(result.plan);
+ assert.equal(result.report.plannedLayers, 24);
+ for (const [axis, center] of [['x', .625], ['z', .3125]] as const) {
+ const cell = Math.floor((center + 1) * 32);
+ const core = result.plan.axes[axis].find(g => g.startCell <= cell && g.endCell > cell)!;
+ const edge = result.plan.axes[axis][0]!;
+ const uniformWidthAtSameBudget = 64 / (24 / 3);
+ assert.ok(core.endCell - core.startCell < uniformWidthAtSameBudget,
+ `${axis} core must receive finer depth placement than equal XYZ allocation at the same budget: ${JSON.stringify(result.plan.axes[axis])}`);
+ assert.ok(edge.endCell - edge.startCell > core.endCell - core.startCell, `${axis} allocation must follow the off-center feature.`);
+ }
+});
+
+test('small complete partitions and the combined budget match an independent exhaustive displacement oracle', () => {
+ // Constant unit emission in a side-2 cube has mass 8 and boundary variation
+ // 8 * cellPitch per reference cell. Integrate physical displacement directly,
+ // without the planner's prefix sums or dynamic-programming recurrence.
+ function alternatives(cells: number) {
+ return Array.from({ length: 2 ** (cells - 1) }, (_, mask) => {
+ const edges = [0, ...Array.from({ length: cells - 1 }, (_, i) => i + 1).filter(i => mask & (1 << (i - 1))), cells];
+ let error = 0;
+ for (let g = 1; g < edges.length; g++) {
+ const first = edges[g - 1]!, end = edges[g]!, midpoint = -1 + (first + end) / cells;
+ for (let cell = first; cell < end; cell++) error +=
+ Math.abs((-1 + 2 * (cell + .5) / cells) - midpoint) * (8 * 2 / cells) * Math.SQRT2 / (2 * 8);
+ }
+ return { layers: edges.length - 1, error };
+ });
+ }
+ const candidates = { x: alternatives(5), y: alternatives(6), z: alternatives(4) };
+ let bestWorst = Infinity, bestSum = Infinity;
+ for (const x of candidates.x) for (const y of candidates.y) for (const z of candidates.z) {
+ if (x.layers + y.layers + z.layers > 9) continue;
+ const worst = Math.max(x.error, y.error, z.error), sum = x.error + y.error + z.error;
+ if (worst < bestWorst - 1e-12 || Math.abs(worst - bestWorst) < 1e-12 && sum < bestSum) { bestWorst = worst; bestSum = sum; }
+ }
+ const result = optimizeVolumeLayers({ ...uniform(), referenceSliceCounts: { x: 5, y: 6, z: 4 }, maximumLayers: 9 });
+ complete(result.plan);
+ const errors = Object.values(result.report.estimatedNormalizedL1);
+ assert.ok(Math.abs(Math.max(...errors) - bestWorst) < 1e-12);
+ assert.ok(Math.abs(errors.reduce((sum, n) => sum + n, 0) - bestSum) < 1e-12);
+});
+
+test('planner rejects invalid or empty emission and responds to cancellation before and during probes', () => {
+ for (const value of [NaN, Infinity, -1]) assert.throws(() => optimizeVolumeLayers({ ...uniform(),
+ sampleEmission(_x, _y, _z, out) { out.fill(value); } }), /finite nonnegative/);
+ assert.throws(() => optimizeVolumeLayers({ ...uniform(), sampleEmission(_x, _y, _z, out) { out.fill(0); } }), /empty probe/);
+ assert.throws(() => optimizeVolumeLayers({ ...uniform(), maximumLayers: 501 }), /Invalid layer optimization input/);
+ const stopped = new Error('cancelled probe');
+ let calls = 0;
+ assert.throws(() => optimizeVolumeLayers({ ...uniform(), signal: { throwIfAborted() { throw stopped; } },
+ sampleEmission(_x, _y, _z, out) { calls++; out.fill(1); } }), error => error === stopped);
+ assert.equal(calls, 0);
+ assert.throws(() => optimizeVolumeLayers({ ...uniform(), signal: { throwIfAborted() { if (calls) throw stopped; } },
+ sampleEmission(_x, _y, _z, out) { calls++; out.fill(1); } }), error => error === stopped);
+ assert.ok(calls > 0 && calls <= 8 * 8 * 4, 'Cancellation is checked again at the next reference cell.');
+});
diff --git a/labs/nebula/packages/volume-core/src/sampling/layer-optimization.ts b/labs/nebula/packages/volume-core/src/sampling/layer-optimization.ts
new file mode 100644
index 0000000000..9b08da3fca
--- /dev/null
+++ b/labs/nebula/packages/volume-core/src/sampling/layer-optimization.ts
@@ -0,0 +1,200 @@
+/** Offline allocation of a global slab budget. Geometry is sampled again at full resolution afterwards. */
+import type { Axis, Bounds3, Vector3 } from '../contracts/volume-recipe.ts';
+import { readVolumeLayerPlan, type VolumeLayerPlan } from '../contracts/volume-slices.ts';
+import type { Cancellation } from '../contracts/cancellation.ts';
+
+export const DEFAULT_VOLUME_LAYER_BUDGET = 500;
+export interface LayerOptimizationReport {
+ schema: 'cssearth-layer-optimization@1';
+ method: 'gradient-weighted-depth-displacement@1';
+ maximumLayers: number;
+ referenceLayers: number;
+ plannedLayers: number;
+ probeWidth: number;
+ probeSubpixels: 2;
+ targetNormalizedL1: number;
+ estimatedNormalizedL1: Record;
+ status: 'target-met' | 'budget-limited';
+}
+export interface LayerOptimizationOptions {
+ bounds: Bounds3;
+ referenceSliceCounts: Record;
+ referenceSamplesPerSlab: number;
+ sampleEmission(x: number, y: number, z: number, out: Vector3): void;
+ maximumLayers?: number;
+ /** Additional depth-collapse error estimate, not the browser handoff acceptance threshold. */
+ targetNormalizedL1?: number;
+ probeWidth?: number;
+ signal?: Cancellation;
+ onProgress?(progress: { completed: number; total: number }): void;
+}
+const AXES: readonly Axis[] = ['x', 'y', 'z'];
+const object = (v: unknown): v is Record => v !== null && typeof v === 'object' && !Array.isArray(v);
+const integer = (v: unknown, min: number, max: number): v is number => typeof v === 'number' && Number.isSafeInteger(v) && v >= min && v <= max;
+const finite = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);
+const sum = (v: Record) => v.x + v.y + v.z;
+
+/** The report describes a coarse planning proxy. It never certifies a material or browser rendering. */
+export function readLayerOptimizationReport(value: unknown, plan: VolumeLayerPlan): LayerOptimizationReport {
+ if (!object(value) || value.schema !== 'cssearth-layer-optimization@1' || value.method !== 'gradient-weighted-depth-displacement@1' ||
+ !integer(value.maximumLayers, 3, DEFAULT_VOLUME_LAYER_BUDGET) || !integer(value.referenceLayers, 3, 1536) ||
+ !integer(value.plannedLayers, 3, value.maximumLayers) || !integer(value.probeWidth, 8, 256) || value.probeSubpixels !== 2 ||
+ !finite(value.targetNormalizedL1) || value.targetNormalizedL1 <= 0 || value.targetNormalizedL1 > .04 ||
+ !object(value.estimatedNormalizedL1))
+ throw new TypeError('Invalid layer optimization report.');
+ const errors = value.estimatedNormalizedL1;
+ if (AXES.some(axis => !finite(errors[axis]) || Number(errors[axis]) < 0)) throw new TypeError('Invalid layer optimization error estimate.');
+ const met = AXES.every(axis => Number(errors[axis]) <= Number(value.targetNormalizedL1));
+ if (value.referenceLayers !== sum(plan.referenceSliceCounts) ||
+ value.plannedLayers !== AXES.reduce((n, axis) => n + plan.axes[axis].length, 0) ||
+ value.status !== (met ? 'target-met' : 'budget-limited')) throw new TypeError('Layer optimization report differs from its plan.');
+ return { schema: value.schema, method: value.method, maximumLayers: value.maximumLayers,
+ referenceLayers: value.referenceLayers, plannedLayers: value.plannedLayers, probeWidth: value.probeWidth, probeSubpixels: 2,
+ targetNormalizedL1: value.targetNormalizedL1, estimatedNormalizedL1: { x: Number(errors.x), y: Number(errors.y), z: Number(errors.z) },
+ status: met ? 'target-met' : 'budget-limited' };
+}
+
+interface Profile { sensitivity: Float64Array; mass: number; pitch: number }
+interface Partitions { costs: Float64Array; backtrack: Int16Array; stride: number }
+function cancelled(signal?: Cancellation) { signal?.throwIfAborted(); }
+
+/** Depth is only regrouped; every reference midpoint is still integrated by the subsequent full bake. */
+export function optimizeVolumeLayers(options: LayerOptimizationOptions): { plan: VolumeLayerPlan; report: LayerOptimizationReport } {
+ const { bounds, referenceSliceCounts: counts, referenceSamplesPerSlab: samples } = options;
+ const budget = options.maximumLayers ?? DEFAULT_VOLUME_LAYER_BUDGET;
+ const target = options.targetNormalizedL1 ?? .01, width = options.probeWidth ?? 64;
+ if (!bounds || !Array.isArray(bounds.min) || !Array.isArray(bounds.max) || bounds.min.length !== 3 || bounds.max.length !== 3 ||
+ bounds.min.some((n, i) => !finite(n) || !finite(bounds.max[i]) || n >= bounds.max[i]!) ||
+ !counts || AXES.some(axis => !integer(counts[axis], 1, 512)) || !integer(samples, 1, 8) ||
+ !integer(budget, 3, DEFAULT_VOLUME_LAYER_BUDGET) || !integer(width, 8, 256) || !finite(target) || target <= 0 || target > .04 ||
+ typeof options.sampleEmission !== 'function') throw new TypeError('Invalid layer optimization input.');
+ cancelled(options.signal);
+ const profiles = {} as Record;
+ let completed = 0;
+ for (const axis of AXES) {
+ const axial = axis === 'x' ? 0 : axis === 'y' ? 1 : 2;
+ const horizontal = axis === 'x' ? 1 : 0, vertical = axis === 'z' ? 1 : 2;
+ const uSpan = bounds.max[horizontal] - bounds.min[horizontal], vSpan = bounds.max[vertical] - bounds.min[vertical];
+ const height = Math.max(8, Math.min(256, Math.round(width * vSpan / uSpan)));
+ const du = uSpan / width, dv = vSpan / height, area = du * dv;
+ const pitch = (bounds.max[axial] - bounds.min[axial]) / counts[axis];
+ const ds = pitch / samples, rgb: Vector3 = [0, 0, 0], map = new Float64Array(width * height);
+ const sensitivity = new Float64Array(counts[axis]);
+ let mass = 0;
+ for (let index = 0; index < counts[axis]; index++) {
+ cancelled(options.signal);
+ const center = bounds.min[axial] + (index + .5) * pitch;
+ map.fill(0);
+ for (let row = 0; row < height; row++) for (let col = 0; col < width; col++) {
+ let light = 0;
+ // Two phases in both image directions reduce accidental alignment with narrow features.
+ for (let sy = 0; sy < 2; sy++) for (let sx = 0; sx < 2; sx++) {
+ const u = bounds.min[horizontal] + (col + (sx + .5) / 2) * du;
+ const v = bounds.max[vertical] - (row + (sy + .5) / 2) * dv;
+ for (let sample = 0; sample < samples; sample++) {
+ const d = center + pitch * ((sample + .5) / samples - .5);
+ if (axis === 'x') options.sampleEmission(d, u, v, rgb);
+ else if (axis === 'y') options.sampleEmission(u, d, v, rgb);
+ else options.sampleEmission(u, v, d, rgb);
+ if (rgb.some(n => !finite(n) || n < 0)) throw new TypeError('Layer planning requires finite nonnegative emission.');
+ light += Math.max(...rgb) * ds / 4;
+ }
+ }
+ if (!finite(light)) throw new TypeError('Layer planning emission overflowed.');
+ map[row * width + col] = light; mass += light * area;
+ }
+ // Total variation includes both external edges. Constant interior light still has a silhouette.
+ let gradient = 0;
+ for (let row = 0; row < height; row++) for (let col = 0; col < width; col++) {
+ const value = map[row * width + col]!;
+ gradient += Math.abs(value - (col ? map[row * width + col - 1]! : 0)) / du;
+ gradient += Math.abs(value - (row ? map[(row - 1) * width + col]! : 0)) / dv;
+ if (col === width - 1) gradient += value / du;
+ if (row === height - 1) gradient += value / dv;
+ }
+ sensitivity[index] = gradient * area;
+ if (!finite(sensitivity[index]) || !finite(mass)) throw new TypeError('Layer planning profile overflowed.');
+ completed++;
+ if (completed % 8 === 0 || completed === sum(counts)) options.onProgress?.({ completed, total: sum(counts) });
+ }
+ if (!(mass > 0)) throw new TypeError(`Layer planning found no emission in the ${axis} probe; an empty probe cannot certify omission.`);
+ profiles[axis] = { sensitivity, mass, pitch };
+ }
+ const partitions = {} as Record;
+ for (const axis of AXES) {
+ const { sensitivity, mass, pitch } = profiles[axis];
+ const weights = new Float64Array(sensitivity.length + 1), moments = new Float64Array(sensitivity.length + 1);
+ for (let i = 0; i < sensitivity.length; i++) {
+ weights[i + 1] = weights[i]! + sensitivity[i]!;
+ moments[i + 1] = moments[i]! + sensitivity[i]! * (i + .5);
+ }
+ // At the corner between XYZ banks, tan(view angle) <= sqrt(2). The probe's spatial
+ // gradient times projected displacement estimates EXTRA L1 from collapsing fine planes.
+ // It excludes RGBA8/browser error and is deliberately not a visual acceptance result.
+ const cost = (start: number, end: number) => {
+ if (end - start === 1) return 0;
+ const center = (start + end) / 2, middle = Math.floor(center);
+ const before = center * (weights[middle]! - weights[start]!) - (moments[middle]! - moments[start]!);
+ const after = moments[end]! - moments[middle]! - center * (weights[end]! - weights[middle]!);
+ return Math.max(0, before + after) * pitch * Math.SQRT2 / (2 * mass);
+ };
+ // A midpoint split can first INCREASE error around a concentrated feature. Greedy
+ // splitting gets trapped there; solve each complete partition before allocating XYZ.
+ const size = counts[axis], maximum = Math.min(size, budget - 2), stride = size + 1;
+ const intervalCosts = new Float64Array(stride * stride);
+ for (let start = 0; start < size; start++) for (let end = start + 1; end <= size; end++) intervalCosts[start * stride + end] = cost(start, end);
+ const backtrack = new Int16Array((maximum + 1) * stride), costs = new Float64Array(maximum + 1).fill(Infinity);
+ let previous = new Float64Array(stride).fill(Infinity); previous[0] = 0;
+ for (let layers = 1; layers <= maximum; layers++) {
+ cancelled(options.signal);
+ const next = new Float64Array(stride).fill(Infinity);
+ for (let end = layers; end <= size; end++) {
+ let best = Infinity, split = -1;
+ for (let start = layers - 1; start < end; start++) {
+ const value = previous[start]! + intervalCosts[start * stride + end]!;
+ if (value < best) { best = value; split = start; }
+ }
+ next[end] = best; backtrack[layers * stride + end] = split;
+ }
+ costs[layers] = next[size]!; previous = next;
+ }
+ partitions[axis] = { costs, backtrack, stride };
+ }
+ const needed = (axis: Axis) => partitions[axis].costs.findIndex(value => value <= target);
+ let selected: Record = { x: needed('x'), y: needed('y'), z: needed('z') };
+ if (AXES.some(axis => selected[axis] < 1) || sum(selected) > budget) {
+ // Enumerate XY counts; for each remaining budget retain the best complete Z partition.
+ // Minimize the worst bank, then total error and count. Stable loop order resolves ties.
+ const bestZ = new Int16Array(budget + 1);
+ for (let remaining = 1; remaining <= budget; remaining++) {
+ const prior = bestZ[remaining - 1]!;
+ bestZ[remaining] = remaining < partitions.z.costs.length && (!prior || partitions.z.costs[remaining]! < partitions.z.costs[prior]!) ? remaining : prior;
+ }
+ let bestWorst = Infinity, bestSum = Infinity, bestCount = Infinity;
+ for (let x = 1; x < partitions.x.costs.length; x++) for (let y = 1; y < partitions.y.costs.length && x + y < budget; y++) {
+ const z = bestZ[budget - x - y]!;
+ if (!z) continue;
+ const errors = [partitions.x.costs[x]!, partitions.y.costs[y]!, partitions.z.costs[z]!];
+ const worst = Math.max(...errors), total = errors.reduce((a, b) => a + b, 0), count = x + y + z;
+ if (worst < bestWorst || worst === bestWorst && (total < bestSum || total === bestSum && count < bestCount)) {
+ selected = { x, y, z }; bestWorst = worst; bestSum = total; bestCount = count;
+ }
+ }
+ }
+ const groups = (axis: Axis) => {
+ const result: { startCell: number; endCell: number }[] = [], { backtrack, stride } = partitions[axis];
+ let endCell = counts[axis];
+ for (let layers = selected[axis]; layers > 0; layers--) {
+ const startCell = backtrack[layers * stride + endCell]!;
+ result.push({ startCell, endCell }); endCell = startCell;
+ }
+ return result.reverse();
+ };
+ const error = { x: partitions.x.costs[selected.x]!, y: partitions.y.costs[selected.y]!, z: partitions.z.costs[selected.z]! }, count = sum(selected);
+ const plan = readVolumeLayerPlan({ schema: 'cssearth-volume-layer-plan@1', referenceSliceCounts: { ...counts },
+ referenceSamplesPerSlab: samples, axes: { x: groups('x'), y: groups('y'), z: groups('z') } });
+ const report = readLayerOptimizationReport({ schema: 'cssearth-layer-optimization@1', method: 'gradient-weighted-depth-displacement@1',
+ maximumLayers: budget, referenceLayers: sum(counts), plannedLayers: count, probeWidth: width, probeSubpixels: 2,
+ targetNormalizedL1: target, estimatedNormalizedL1: error, status: AXES.every(axis => error[axis] <= target) ? 'target-met' : 'budget-limited' }, plan);
+ return { plan, report };
+}
diff --git a/labs/nebula/sources/lmc-smash-full.webp.json b/labs/nebula/sources/lmc-smash-full.webp.json
index c1f7238d40..e85b3fd5bc 100644
--- a/labs/nebula/sources/lmc-smash-full.webp.json
+++ b/labs/nebula/sources/lmc-smash-full.webp.json
@@ -17,6 +17,6 @@
"sha256": "7e29b020ac5de2634a909d9b1adc45eea8ca482dd5681464448b1fe26afbbcd0"
}
},
- "interpretation": "Full spatial resolution, converted from publisher 16-bit TIFF to 8-bit sRGB WebP. Lossy display reference; no crop or resize. Original TIFF remains in the ignored cache.",
+ "interpretation": "Converted from the publisher's 16-bit TIFF to 8-bit sRGB WebP. Lossy display reference; never cropped. Full spatial resolution: not resized. Original TIFF remains in the ignored cache.",
"outputBytes": 28128434
}
diff --git a/labs/nebula/sources/omega-centauri-vst.webp.json b/labs/nebula/sources/omega-centauri-vst.webp.json
new file mode 100644
index 0000000000..03b949abfb
--- /dev/null
+++ b/labs/nebula/sources/omega-centauri-vst.webp.json
@@ -0,0 +1,23 @@
+{
+ "schema": "cssearth-lab-reference-image@1",
+ "source": {
+ "id": "eso1119b",
+ "url": "https://cdn.eso.org/images/original/eso1119b.tif",
+ "sha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "bytes": 555370438,
+ "width": 14540,
+ "height": 14540,
+ "publisherUrl": "https://www.eso.org/public/images/eso1119b/",
+ "credit": "ESO/INAF-VST/OmegaCAM. Acknowledgement: A. Grado, L. Limatola/INAF-Capodimonte Observatory",
+ "license": "CC-BY-4.0",
+ "output": {
+ "path": "labs/nebula/sources/omega-centauri-vst.webp",
+ "quality": 92,
+ "effort": 5,
+ "resizeWidth": 8192,
+ "sha256": "6e30ef43b59f2ca3a8ac076bc7f2e9b1da801b9665df027feb5e3018f331a1a8"
+ }
+ },
+ "interpretation": "Converted from the publisher's 8-bit TIFF to 8-bit sRGB WebP. Lossy display reference; never cropped. Reduced from 14540px to 8192px wide because libwebp cannot encode this source at native size; measure angular scale from the recorded native dimensions, not from this file. Original TIFF remains in the ignored cache.",
+ "outputBytes": 20914012
+}
diff --git a/labs/nebula/sources/omega-centauri-wfi.webp.json b/labs/nebula/sources/omega-centauri-wfi.webp.json
new file mode 100644
index 0000000000..675d9e3558
--- /dev/null
+++ b/labs/nebula/sources/omega-centauri-wfi.webp.json
@@ -0,0 +1,22 @@
+{
+ "schema": "cssearth-lab-reference-image@1",
+ "source": {
+ "id": "eso0844a",
+ "url": "https://cdn.eso.org/images/original/eso0844a.tif",
+ "sha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429",
+ "bytes": 147797270,
+ "width": 8040,
+ "height": 7560,
+ "publisherUrl": "https://www.eso.org/public/images/eso0844a/",
+ "credit": "ESO",
+ "license": "CC-BY-4.0",
+ "output": {
+ "path": "labs/nebula/sources/omega-centauri-wfi.webp",
+ "quality": 92,
+ "effort": 5,
+ "sha256": "2a8a8954a58915f1ce58951402d5e71676cc48de23fafe415a9b86b9d8ceeeef"
+ }
+ },
+ "interpretation": "Converted from the publisher's 8-bit TIFF to 8-bit sRGB WebP. Lossy display reference; never cropped. Full spatial resolution: not resized. Original TIFF remains in the ignored cache.",
+ "outputBytes": 18834346
+}
diff --git a/labs/nebula/sources/reference-images.json b/labs/nebula/sources/reference-images.json
index d1b6d9f9f5..414b2b08c6 100644
--- a/labs/nebula/sources/reference-images.json
+++ b/labs/nebula/sources/reference-images.json
@@ -34,6 +34,41 @@
"effort": 5,
"sha256": "3ed2fd1ae7729b860f00d2bd7d0c13b6a05f81c8d66e587e626cc4e2bac92698"
}
+ },
+ {
+ "id": "eso1119b",
+ "url": "https://cdn.eso.org/images/original/eso1119b.tif",
+ "sha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "bytes": 555370438,
+ "width": 14540,
+ "height": 14540,
+ "publisherUrl": "https://www.eso.org/public/images/eso1119b/",
+ "credit": "ESO/INAF-VST/OmegaCAM. Acknowledgement: A. Grado, L. Limatola/INAF-Capodimonte Observatory",
+ "license": "CC-BY-4.0",
+ "output": {
+ "path": "labs/nebula/sources/omega-centauri-vst.webp",
+ "quality": 92,
+ "effort": 5,
+ "resizeWidth": 8192,
+ "sha256": "6e30ef43b59f2ca3a8ac076bc7f2e9b1da801b9665df027feb5e3018f331a1a8"
+ }
+ },
+ {
+ "id": "eso0844a",
+ "url": "https://cdn.eso.org/images/original/eso0844a.tif",
+ "sha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429",
+ "bytes": 147797270,
+ "width": 8040,
+ "height": 7560,
+ "publisherUrl": "https://www.eso.org/public/images/eso0844a/",
+ "credit": "ESO",
+ "license": "CC-BY-4.0",
+ "output": {
+ "path": "labs/nebula/sources/omega-centauri-wfi.webp",
+ "quality": 92,
+ "effort": 5,
+ "sha256": "2a8a8954a58915f1ce58951402d5e71676cc48de23fafe415a9b86b9d8ceeeef"
+ }
}
]
}
diff --git a/labs/nebula/sources/smc-smash-full.webp.json b/labs/nebula/sources/smc-smash-full.webp.json
index 8b2fb57973..bd31817107 100644
--- a/labs/nebula/sources/smc-smash-full.webp.json
+++ b/labs/nebula/sources/smc-smash-full.webp.json
@@ -17,6 +17,6 @@
"sha256": "3ed2fd1ae7729b860f00d2bd7d0c13b6a05f81c8d66e587e626cc4e2bac92698"
}
},
- "interpretation": "Full spatial resolution, converted from publisher 16-bit TIFF to 8-bit sRGB WebP. Lossy display reference; no crop or resize. Original TIFF remains in the ignored cache.",
+ "interpretation": "Converted from the publisher's 16-bit TIFF to 8-bit sRGB WebP. Lossy display reference; never cropped. Full spatial resolution: not resized. Original TIFF remains in the ignored cache.",
"outputBytes": 7072836
}
diff --git a/packages/catalog/src/nebulae.test.ts b/packages/catalog/src/nebulae.test.ts
index 691ed0cc6b..4b9a763a9e 100644
--- a/packages/catalog/src/nebulae.test.ts
+++ b/packages/catalog/src/nebulae.test.ts
@@ -4,10 +4,20 @@ const fixture=()=>({schema:'cssearth-nebula-catalog@1',frame:{referenceFrame:'su
sources:[{id:'paper',url:'https://example.org/paper',sha256:'a'.repeat(64),bytes:10,citation:'Original measurement'}],
objects:[{id:'fixture',kind:'nebula',name:'Example',aliases:[],positionM:[3.085677581491367e18,0,0],skyPosition:{raDeg:0,decDeg:0,sourceRef:'paper'},
distance:{valuePc:100,sourceRef:'paper',method:'Parallax'},classification:{name:'Emission nebula',basis:'Observed extended emission; modeled depth.',sourceRef:'paper'},status:'confirmed',detailedObjectId:'fixture'}]});
-test('nebula records preserve scientific type and are never classified as galaxies',()=>{
+test('nebula records preserve identity in the Galactic volume transport',()=>{
const input=fixture(), parsed=parsePreparedNebulaCatalog(input);expect(parsed).toBe(input);expect(isPreparedNebula(parsed.objects[0]!)).toBe(true);
expect(isPreparedNebula({})).toBe(false);
});
+test('the shared Galactic volume transport preserves globular-cluster identity and source requirements', () => {
+ const input=fixture(); input.objects[0]!.kind='globular-cluster';
+ const parsed=parsePreparedNebulaCatalog(input);
+ expect(parsed.objects[0]!.kind).toBe('globular-cluster');
+ expect(isPreparedNebula(parsed.objects[0]!)).toBe(true);
+ input.objects[0]!.classification.sourceRef='missing';
+ expect(()=>parsePreparedNebulaCatalog(input)).toThrow(/Unknown nebula source reference/);
+ input.objects[0]!.classification.sourceRef='paper'; input.objects[0]!.kind='galaxy';
+ expect(()=>parsePreparedNebulaCatalog(input)).toThrow(/Invalid nebula identity/);
+});
test('nebula source references, sky domain and distance are guarded',()=>{
for(const change of [(v:ReturnType)=>{v.objects[0]!.skyPosition.raDeg=360;},
(v:ReturnType)=>{v.objects[0]!.distance.valuePc=0;},
diff --git a/packages/catalog/src/nebulae.ts b/packages/catalog/src/nebulae.ts
index a6ddea6cc4..b0b6bb5d4d 100644
--- a/packages/catalog/src/nebulae.ts
+++ b/packages/catalog/src/nebulae.ts
@@ -1,10 +1,11 @@
import { validateSpatialPosition } from './spatial-relations.ts';
import type { PreparedGalaxyRecord, SpatialCatalogSource } from './spatial.js';
-/** A sourced centre and display model; reconstructed depth is not a measured density. */
+/** A sourced Galactic volume. The legacy nebula transport also carries stellar clusters;
+ * the row kind preserves their scientific identity independently of the shared renderer. */
export interface PreparedNebulaRecord extends Pick {
- readonly kind: 'nebula';
+ readonly kind: 'nebula' | 'globular-cluster';
readonly detailedObjectId: string;
readonly classification: { readonly name: string; readonly basis: string; readonly sourceRef: string };
}
@@ -15,7 +16,7 @@ export interface PreparedNebulaCatalog {
readonly objects: readonly PreparedNebulaRecord[];
}
export function isPreparedNebula(object: object): object is PreparedNebulaRecord {
- return 'kind' in object && object.kind === 'nebula';
+ return 'kind' in object && (object.kind === 'nebula' || object.kind === 'globular-cluster');
}
const record = (v: unknown): Record => {
if (!v || typeof v !== 'object' || Array.isArray(v)) throw new TypeError('Expected nebula catalogue object.');
@@ -26,7 +27,7 @@ const finite = (v: unknown): number => { if (typeof v !== 'number' || !Number.is
const positive = (v: unknown): number => { const n = finite(v); if (n <= 0) throw new TypeError('Expected positive nebula value.'); return n; };
const array = (v: unknown): readonly unknown[] => { if (!Array.isArray(v)) throw new TypeError('Expected nebula array.'); return v; };
-/** A separate catalogue avoids misclassifying Galactic nebulae as Local Group galaxies. */
+/** A separate catalogue avoids misclassifying Galactic volumes as Local Group galaxies. */
export function parsePreparedNebulaCatalog(input: unknown): PreparedNebulaCatalog {
const v = record(input), frame = record(v.frame), sources = array(v.sources), objects = array(v.objects);
if (v.schema !== 'cssearth-nebula-catalog@1' || objects.length > 10000) throw new TypeError('Invalid nebula catalogue.');
@@ -41,7 +42,7 @@ export function parsePreparedNebulaCatalog(input: unknown): PreparedNebulaCatalo
const reference = (v: unknown) => { const id = text(v); if (![...sourceIds].some(source => id === source || id.startsWith(`${source}:`))) throw new TypeError('Unknown nebula source reference.'); };
for (const value of objects) {
const row = record(value), id = text(row.id), detail = text(row.detailedObjectId);
- if (row.kind !== 'nebula' || row.status !== 'confirmed' || ids.has(id) || details.has(detail) ||
+ if (!isPreparedNebula(row) || row.status !== 'confirmed' || ids.has(id) || details.has(detail) ||
!/^[a-z][a-z0-9-]*$/.test(id) || !/^[a-z][a-z0-9-]*$/.test(detail)) throw new TypeError('Invalid nebula identity.');
ids.add(id); details.add(detail); text(row.name); array(row.aliases).forEach(text);
const position = array(row.positionM); if (position.length !== 3) throw new TypeError('Nebula position requires three metre coordinates.'); position.forEach(finite);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 429ee92607..a484b4787e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -187,6 +187,9 @@ importers:
react-dom:
specifier: 19.2.8
version: 19.2.8(react@19.2.8)
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
vite:
specifier: 7.3.1
version: 7.3.1(@types/node@26.4.0)(jiti@2.6.1)(lightningcss@1.33.0)(yaml@2.9.0)
diff --git a/site/prepared-context-navigation.mts b/site/prepared-context-navigation.mts
index 0bb6e454ef..a0b0f99a76 100644
--- a/site/prepared-context-navigation.mts
+++ b/site/prepared-context-navigation.mts
@@ -96,6 +96,19 @@ export function createPreparedContextNavigation({ layer, presentation, sources =
unsubscribeLens?.(); unsubscribeLens = null; lensObjectId = objectId;
if (objectId && !layer.imageLayerFrames?.[objectId]) unsubscribeLens = layer.subscribeVolumeLens?.(objectId, publishLens) ?? null;
};
+ const prepareFocus = (id: string | null): Promise | undefined => {
+ const object = id ? layer.resolveGalaxy(id) : null;
+ if (id && !object) throw new TypeError(`Unknown prepared galaxy focus: ${id}`);
+ const objectId = object && !isPreparedCluster(object) ? object.detailedObjectId : undefined;
+ if (!objectId || unavailableObjectIds.includes(objectId) || layer.imageLayerFrames?.[objectId] ||
+ !layer.volumeLensFrames?.[objectId] || layer.volumeLensState(objectId)) return;
+ // The subscription pins the bank while loading; otherwise warm-residency trimming could evict it
+ // before the focus consumer reads its authored radius. Await the same load, including its errors.
+ observeLens(id);
+ return layer.ensureVolumeLens(objectId).then(() => {
+ if (!layer.volumeLensState(objectId)) throw new TypeError(`Prepared volume lens bank did not become ready: ${objectId}`);
+ });
+ };
const resolve = (id: string): PreparedNavigationFocus => {
const object = layer.resolveGalaxy(id);
if (!object) throw new TypeError(`Unknown prepared galaxy focus: ${id}`);
@@ -110,8 +123,8 @@ export function createPreparedContextNavigation({ layer, presentation, sources =
};
const publishSelection = () => {
if (!ready || !navigation) return;
- const next = navigation.preparedFocus?.()?.id ?? null;
- layer.selectGalaxy(next);
+ const focus = navigation.preparedFocus?.() ?? null, next = focus?.id ?? null;
+ layer.selectGalaxy(next, focus);
if (next === selected) return;
selected = next; observeLens(next);
publishContent(next);
@@ -130,12 +143,22 @@ export function createPreparedContextNavigation({ layer, presentation, sources =
},
suspend() { ready = false; flight?.abort(); },
restore(url: string | URL) {
- if (!navigation?.setPreparedFocus) return;
+ const owner = navigation;
+ if (!owner?.setPreparedFocus) return;
+ flight?.abort(); const controller = new AbortController(); flight = controller;
+ const current = () => navigation === owner && flight === controller && !controller.signal.aborted;
ready = false;
const query = new URL(url, windowTarget.location.href).searchParams;
- try {
- if (query.getAll('focus').length > 1) throw new TypeError('A saved view may have only one prepared focus.');
- if (query.getAll('focusLens').length > 1) throw new TypeError('A saved view may have only one prepared focus lens.');
+ const finish = () => { if (current()) { ready = true; flight = null; } };
+ const fail = (error: unknown) => {
+ if (!current()) return;
+ selected = owner.preparedFocus?.()?.id ?? null;
+ layer.selectGalaxy(selected, owner.preparedFocus?.() ?? null); observeLens(selected);
+ publishContent(selected);
+ onError(error);
+ };
+ const apply = () => {
+ if (!current()) return;
const id = query.get('focus'), focus = id ? resolve(id) : null, state = lensState(id);
const lensId = query.get('focusLens') ?? state?.defaultLens;
const object = id ? layer.resolveGalaxy(id) : null;
@@ -143,41 +166,52 @@ export function createPreparedContextNavigation({ layer, presentation, sources =
if (id && !unavailable && lensId !== undefined && (!state || !state.lenses.some(lens => lens.id === lensId))) {
throw new TypeError(`Unknown prepared focus lens: ${lensId}`);
}
- navigation.setPreparedFocus(focus);
+ owner.setPreparedFocus!(focus);
if (state && lensId !== undefined && !layer.imageLayerFrames?.[state.objectId]) layer.selectVolumeLens(state.objectId, lensId);
- selected = id; layer.selectGalaxy(id); observeLens(id);
+ selected = id; layer.selectGalaxy(id, focus); observeLens(id);
publishContent(id);
if (!id || (state && !query.has('focusLens'))) writeSelectionUrl(id);
+ ready = true;
// A focus-only link is a destination. Saved camera links retain their
// exact observer pose; changing the pivot alone must not reframe them.
if (focus && !query.has('v')) {
- flight?.abort();
- const controller = new AbortController(); flight = controller;
beforeFlight();
- void navigation.flyToPreparedFocus(focus, { signal: controller.signal, reducedMotion: true })
- .catch(error => { if (!controller.signal.aborted) onError(error); })
- .finally(() => { if (flight === controller) flight = null; });
+ if (current()) return owner.flyToPreparedFocus(focus, { signal: controller.signal, reducedMotion: true });
}
- } catch (error) {
- selected = navigation.preparedFocus?.()?.id ?? null;
- layer.selectGalaxy(selected); observeLens(selected);
- publishContent(selected);
- onError(error);
- }
- finally { ready = true; }
+ };
+ try {
+ if (query.getAll('focus').length > 1) throw new TypeError('A saved view may have only one prepared focus.');
+ if (query.getAll('focusLens').length > 1) throw new TypeError('A saved view may have only one prepared focus lens.');
+ const loading = prepareFocus(query.get('focus'));
+ if (loading) return loading.then(apply).catch(fail).finally(finish);
+ const flying = apply();
+ if (flying) return flying.catch(fail).finally(finish);
+ } catch (error) { fail(error); }
+ finish();
},
async select(object: Pick) {
- if (!navigation?.flyToPreparedFocus) return;
+ const owner = navigation;
+ if (!owner?.flyToPreparedFocus) return;
flight?.abort(); const controller = new AbortController(); flight = controller;
+ const current = () => navigation === owner && flight === controller && !controller.signal.aborted;
try {
+ ready = false;
+ const loading = prepareFocus(object.id);
+ if (loading) await loading;
+ if (!current()) return;
const focus = resolve(object.id);
beforeFlight();
+ if (!current()) return;
ready = true;
- await navigation.flyToPreparedFocus(focus, { signal: controller.signal,
+ await owner.flyToPreparedFocus(focus, { signal: controller.signal,
reducedMotion: windowTarget.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true });
- publishSelection();
- } catch (error) { if (!record(error) || error.name !== 'AbortError') onError(error); }
- finally { if (flight === controller) flight = null; }
+ if (current()) publishSelection();
+ } catch (error) {
+ if (current()) {
+ observeLens(owner.preparedFocus?.()?.id ?? null);
+ if (!record(error) || error.name !== 'AbortError') onError(error);
+ }
+ } finally { if (current()) { ready = true; flight = null; } }
},
destroy() { flight?.abort(); unsubscribe?.(); unsubscribeLens?.(); unsubscribeLens = null; lensObjectId = null; navigation = null; ready = false; },
});
diff --git a/site/prepared-focus-card.mts b/site/prepared-focus-card.mts
index 28e580dc19..3bf0dec3ee 100644
--- a/site/prepared-focus-card.mts
+++ b/site/prepared-focus-card.mts
@@ -94,7 +94,7 @@ export function createPreparedFocusCard(root: HTMLElement | null, showTab: (id:
if (fields.uncertainty.parentElement) fields.uncertainty.parentElement.hidden = !uncertainty && minusPc === undefined && plusPc === undefined;
if (fields.membership.parentElement) fields.membership.parentElement.hidden = cluster;
write('membership', cluster ? 'Galaxy cluster' : nebula ? 'Milky Way' : words(record.membership.group));
- write('association', cluster ? `${record.redshift.value}` : nebula ? 'Galactic nebula' : words(record.membership.subgroup));
+ write('association', cluster ? `${record.redshift.value}` : nebula ? record.kind === 'globular-cluster' ? 'Galactic globular cluster' : 'Galactic nebula' : words(record.membership.subgroup));
write('basis', cluster || nebula ? `${record.classification.basis} ${record.distance.method}` : record.membership.basis);
write('reference', `Distance reference: ${record.distance.sourceRef}`);
for (const [index, link] of links.entries()) {
diff --git a/site/prepared-focus-object.mts b/site/prepared-focus-object.mts
index 9a89ca6322..a825dff4e3 100644
--- a/site/prepared-focus-object.mts
+++ b/site/prepared-focus-object.mts
@@ -9,7 +9,7 @@ export interface PreparedFocusObject {
readonly focusId: string;
readonly name: string;
readonly searchNames: readonly string[];
- readonly classification: 'galaxy' | 'galaxy-cluster' | 'nebula';
+ readonly classification: 'galaxy' | 'galaxy-cluster' | 'nebula' | 'globular-cluster';
readonly systemName: string;
readonly route: string;
readonly sceneHostId: string;
@@ -25,7 +25,7 @@ export function definePreparedFocus(input: unknown): PreparedFocusObject {
typeof input.sceneHostId !== 'string' || !/^[a-z][a-z0-9-]*$/u.test(input.sceneHostId) ||
input.route !== `/${input.sceneHostId}/?focus=${encodeURIComponent(input.id)}` ||
typeof input.name !== 'string' || !input.name || typeof input.systemName !== 'string' || !input.systemName ||
- (input.classification !== 'galaxy' && input.classification !== 'galaxy-cluster' && input.classification !== 'nebula') ||
+ (input.classification !== 'galaxy' && input.classification !== 'galaxy-cluster' && input.classification !== 'nebula' && input.classification !== 'globular-cluster') ||
!Array.isArray(input.searchNames) || !input.searchNames.length || !input.searchNames.every(name => typeof name === 'string' && name)) {
throw new TypeError('Invalid prepared focus destination.');
}
diff --git a/site/test/galactic-focus.test.mts b/site/test/galactic-focus.test.mts
new file mode 100644
index 0000000000..b8ff7bf773
--- /dev/null
+++ b/site/test/galactic-focus.test.mts
@@ -0,0 +1,38 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+import { readPreparedFocusObjects } from '../../tools/prepare-navigation-destinations.mts';
+import { searchObjects } from '../object-search.mts';
+
+test('a source-owned globular cluster is discovered, classified and searched through the shared focus route', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'galactic-focus-'));
+ try {
+ const source = join(root, 'test-cluster/source');
+ await mkdir(source, { recursive: true });
+ await writeFile(join(source, 'nebula.json'), JSON.stringify({
+ schema: 'cssearth-nebula-catalog@1', frame: { referenceFrame: 'sun-icrf', epochJdTt: 2461286.5 },
+ sources: [{ id: 'paper', url: 'https://example.org/paper', sha256: 'a'.repeat(64), bytes: 10, citation: 'Test measurement' }],
+ objects: [{ id: 'test-cluster', kind: 'globular-cluster', name: 'Test cluster', aliases: ['Cluster alias'],
+ positionM: [3.085677581491367e18, 0, 0], skyPosition: { raDeg: 0, decDeg: 0, sourceRef: 'paper' },
+ distance: { valuePc: 100, sourceRef: 'paper', method: 'Test distance' },
+ classification: { name: 'Globular cluster', basis: 'Integrated stellar light.', sourceRef: 'paper' },
+ status: 'confirmed', detailedObjectId: 'test-cluster' }],
+ }));
+ const [focus, ...others] = await readPreparedFocusObjects(root, 'sun');
+ assert.equal(others.length, 0);
+ assert.ok(focus);
+ assert.equal(focus.classification, 'globular-cluster');
+ assert.equal(focus.systemName, 'Milky Way');
+ assert.equal(focus.route, '/sun/?focus=test-cluster');
+ assert.equal(focus.distance.quantity, 'catalogue');
+ const labels = [{ name: focus.name.toLowerCase(), names: focus.searchNames, classification: focus.classification,
+ classificationName: 'globular cluster', systemName: focus.systemName.toLowerCase() }];
+ assert.equal(searchObjects(labels, 'globular clusters').matches.length, 1);
+ assert.equal(searchObjects(labels, 'Cluster alias').matches.length, 1);
+ assert.equal(searchObjects(labels, 'nebula').matches.length, 0);
+ } finally {
+ await rm(root, { recursive: true, force: true });
+ }
+});
diff --git a/site/test/prepared-context-navigation.test.mts b/site/test/prepared-context-navigation.test.mts
index faa0570ec2..81826f8896 100644
--- a/site/test/prepared-context-navigation.test.mts
+++ b/site/test/prepared-context-navigation.test.mts
@@ -11,7 +11,7 @@ import type { DensityVolumeFrame } from '@cssearth/objects';
type ContextLayer = Parameters[0]['layer'];
type Content = { record: PreparedCatalogObject | null; references: readonly SpatialCitation[]; presentation: PreparedFocusPresentation | null };
type FixtureOptions = { object?: Partial & Partial>;
- unavailableObjectIds?: readonly string[];
+ unavailableObjectIds?: readonly string[]; bankReady?: Promise; loadedFramingRadiusUnits?: number;
imageLayerFrames?: ContextLayer['imageLayerFrames']; volumeLensFrames?: ContextLayer['volumeLensFrames']; volumeBank?: PreparedVolumeLensState | null };
const baseFrame: DensityVolumeFrame = { referenceFrame: 'sun-icrf', epochJdTt: 1, originM: [0,0,0], localToReferenceXyzw: [0,0,0,1],
metersPerUnit: 1e18, boundsUnits: { min: [-500,-500,-500], max: [500,500,500] } };
@@ -19,9 +19,10 @@ function required(value: T | null | undefined): T { assert.ok(value !== null
function last(values: T[]): T { return required(values.at(-1)); }
-function fixture({ object = {}, imageLayerFrames = {}, volumeLensFrames = {}, volumeBank = null, unavailableObjectIds = [] }: FixtureOptions = {}) {
+function fixture({ object = {}, imageLayerFrames = {}, volumeLensFrames = {}, volumeBank = null, unavailableObjectIds = [], bankReady, loadedFramingRadiusUnits }: FixtureOptions = {}) {
let current: PreparedNavigationFocus | null = null, signal: AbortSignal | undefined;
- const flights: {id:string; reducedMotion?:boolean}[] = [];
+ const flights: {id:string; reducedMotion?:boolean}[] = [], flightFocuses: PreparedNavigationFocus[] = [];
+ const presentationFocuses: (PreparedNavigationFocus | null | undefined)[] = [];
const callbacks = new Set<() => void>(), errors: Error[] = [], selections: (string | null)[] = [], writes: (string | URL)[] = [], content: Content[] = [];
const windowTarget = { location: new URL('https://example.test/mercury/?focus=catalogue:a&v=saved'),
history: { state: {}, replaceState(state: unknown, _: string, url?: string | URL | null) { assert.ok(url); windowTarget.location = new URL(url, windowTarget.location); writes.push(url); } } };
@@ -29,23 +30,30 @@ function fixture({ object = {}, imageLayerFrames = {}, volumeLensFrames = {}, vo
setPreparedFocus(focus: PreparedNavigationFocus | null) { current = focus; for (const callback of callbacks) callback(); },
subscribe(callback: () => void) { callbacks.add(callback); callback(); return () => callbacks.delete(callback); },
flyToPreparedFocus(focus: PreparedNavigationFocus, options: { signal?: AbortSignal; reducedMotion?: boolean } = {}) { signal = required(options.signal); this.setPreparedFocus(focus);
- flights.push({id:focus.id,reducedMotion:options.reducedMotion});
+ flights.push({id:focus.id,reducedMotion:options.reducedMotion}); flightFocuses.push(focus);
if (options.reducedMotion) return Promise.resolve({completed:true});
return new Promise<{ completed: boolean }>(resolve => required(signal).addEventListener('abort', () => resolve({ completed: false }), { once: true })); } };
// The real `subscribeVolumeLens` hands the listener the bank state, so a deferred bank can replay a
// notification once its payload arrives; a no-argument listener no longer satisfies that signature.
const lensCallbacks = new Set<(state: PreparedVolumeLensState) => void>(), lensWrites: string[] = [];
- let bankState = volumeBank;
+ let bankState = volumeBank, currentVolumeLensFrames = volumeLensFrames;
const applyBank = (change: Partial) => { bankState = { ...required(bankState), ...change }; for (const callback of lensCallbacks) callback(required(bankState)); };
- const layer = { imageLayerFrames, volumeLensFrames,
+ const layer = { imageLayerFrames, get volumeLensFrames() { return currentVolumeLensFrames; },
+ async ensureVolumeLens(objectId: string) {
+ if (!bankReady) return;
+ bankState = await bankReady;
+ assert.equal(bankState.objectId, objectId);
+ if (loadedFramingRadiusUnits !== undefined) currentVolumeLensFrames = { ...currentVolumeLensFrames, [objectId]: { ...required(currentVolumeLensFrames[objectId]), framingRadiusUnits: loadedFramingRadiusUnits } };
+ for (const callback of lensCallbacks) callback(bankState);
+ },
volumeLensState: (objectId: string) => objectId === bankState?.objectId ? bankState : null,
selectVolumeLens(objectId: string, id: string) {
assert.equal(objectId, required(bankState).objectId); assert.ok(required(bankState).lenses.some(lens => lens.id === id));
lensWrites.push(id); applyBank({ id, selectedLens: id });
},
setVolumeStarsVisible(objectId: string, enabled: boolean) { assert.equal(objectId, required(bankState).objectId); applyBank({ starsVisible: enabled }); },
- subscribeVolumeLens(objectId: string, listener: (state: PreparedVolumeLensState) => void) { assert.equal(objectId, required(bankState).objectId); lensCallbacks.add(listener); return () => { lensCallbacks.delete(listener); }; },
- selectGalaxy: (id: string | null) => selections.push(id),
+ subscribeVolumeLens(objectId: string, listener: (state: PreparedVolumeLensState) => void) { assert.equal(objectId, bankState?.objectId ?? Object.keys(currentVolumeLensFrames)[0]); lensCallbacks.add(listener); return () => { lensCallbacks.delete(listener); }; },
+ selectGalaxy: (id: string | null, focus?: PreparedNavigationFocus | null) => { selections.push(id); presentationFocuses.push(focus); },
resolveGalaxy: (id: string): PreparedCatalogObject | null => {
if (!['catalogue:a','catalogue:b'].includes(id)) return null;
const galaxy: PreparedGalaxyRecord = { id, name: id, aliases: [], status: 'confirmed', positionM: [1e20,0,0],
@@ -56,7 +64,7 @@ function fixture({ object = {}, imageLayerFrames = {}, volumeLensFrames = {}, vo
return { ...galaxy, kind: 'galaxy-cluster', classification: required(object.classification),
redshift: { value: .01, type: 'spectroscopic', sourceRef: 'positions:row' },
aperture: { definition: 'R500', properRadiusM: 1e22, comovingRadiusM: 1e22, sourceRef: 'positions:row' } };
- } } satisfies Pick & { volumeLensState(id: string): PreparedVolumeLensState | null };
+ } } satisfies Pick & { volumeLensState(id: string): PreparedVolumeLensState | null };
const sources: SpatialCatalogSource[] = [{ id: 'positions', url: 'https://example.test/positions', sha256: '0'.repeat(64), bytes: 1, citation: 'Published positions', references: [{ id: 'PublishedBibliographicKey', url: 'https://example.test/paper', citation: 'Distance paper' }] },
{ id: 'membership', url: 'https://example.test/membership', sha256: '0'.repeat(64), bytes: 1, citation: 'Published membership' },
{ id: 'unrelated', url: 'https://example.test/unrelated', sha256: '0'.repeat(64), bytes: 1, citation: 'Unused audit input' }];
@@ -64,7 +72,7 @@ function fixture({ object = {}, imageLayerFrames = {}, volumeLensFrames = {}, vo
const controller = createPreparedContextNavigation({ layer: layer as unknown as ContextLayer, windowTarget: windowTarget as unknown as Window, onError: error => { assert.ok(error instanceof Error); errors.push(error); },
sources, unavailableObjectIds, presentation: { metersPerParsec: 3e16, defaultFocusRadiusM: 1e18, minimumDistanceRadii: .01, maximumDistanceM: 1e23 } });
controller.connect(owner as unknown as ObjectWorldNavigation, { onFocusContentChange: (record, references, presentation) => content.push({ record, references, presentation }) });
- return { controller, owner, layer, lensCallbacks, lensWrites, windowTarget, errors, selections, writes, callbacks, content, flights, signal: () => signal };
+ return { controller, owner, layer, lensCallbacks, lensWrites, windowTarget, errors, selections, presentationFocuses, writes, callbacks, content, flights, flightFocuses, signal: () => signal };
}
test('a saved lens link to an unavailable package still opens its actual catalogue record', () => {
@@ -264,3 +272,85 @@ test('an image-layer focus exposes its single optical dataset without volume-onl
assert.deepEqual(f.lensWrites, []);
f.controller.destroy();
});
+
+function deferredBank() {
+ let accept!: (bank: PreparedVolumeLensState) => void, reject!: (error: Error) => void;
+ const promise = new Promise((resolve, fail) => { accept = resolve; reject = fail; });
+ return { promise, accept, reject };
+}
+function coldVolumeFixture() {
+ const load = deferredBank();
+ const f = fixture({ object: { detailedObjectId: 'detailed' },
+ volumeLensFrames: { detailed: { frame: baseFrame, framingRadiusUnits: 5268.814 } },
+ bankReady: load.promise, loadedFramingRadiusUnits: 1650 });
+ return { ...f, load };
+}
+
+test('cold native focus waits for the real bank framing before choosing a destination', async () => {
+ const f = coldVolumeFixture();
+ const restoring = f.controller.restore('https://example.test/sun/?focus=catalogue:a');
+ assert.deepEqual(f.flights, [], 'Loading may not fly using descriptor fallback bounds');
+ assert.equal(f.owner.preparedFocus(), null);
+ f.load.accept(volumeBank()); await restoring;
+ assert.equal(last(f.flightFocuses).framingRadiusM, 1650 * baseFrame.metersPerUnit);
+ assert.deepEqual(f.flights, [{id:'catalogue:a',reducedMotion:true}]);
+ assert.deepEqual(f.errors, []); f.controller.destroy();
+});
+
+test('ordinary cold selection waits for the same authored framing and cancellation prevents stale focus', async () => {
+ const f = coldVolumeFixture();
+ const selecting = f.controller.select({id:'catalogue:a'});
+ assert.deepEqual(f.flights, []);
+ f.load.accept(volumeBank()); await new Promise(resolve => setImmediate(resolve));
+ assert.equal(last(f.flightFocuses).framingRadiusM, 1650 * baseFrame.metersPerUnit);
+ f.controller.suspend(); await selecting; f.controller.destroy();
+ const cancelled = coldVolumeFixture();
+ const stale = cancelled.controller.select({id:'catalogue:a'});
+ cancelled.controller.suspend();
+ cancelled.owner.setPreparedFocus({id:'newer-focus',positionM:[0,0,0],framingRadiusM:1,limits:{minimumDistanceM:1,maximumDistanceM:2}});
+ cancelled.load.accept(volumeBank()); await new Promise(resolve => setImmediate(resolve));
+ assert.deepEqual(cancelled.flights, []); await stale;
+ assert.equal(cancelled.owner.preparedFocus()?.id, 'newer-focus');
+ assert.deepEqual(cancelled.errors, []); cancelled.controller.destroy();
+});
+
+test('cold saved camera keeps its pose while waiting for an explicitly selected lens', async () => {
+ const f = coldVolumeFixture();
+ const restoring = f.controller.restore('https://example.test/sun/?focus=catalogue:a&focusLens=second&v=exact-saved-pose');
+ assert.deepEqual(f.errors, []); assert.deepEqual(f.flights, []);
+ f.load.accept(volumeBank()); await restoring;
+ assert.deepEqual(f.flights, []); assert.equal(f.owner.preparedFocus()?.framingRadiusM,1650 * baseFrame.metersPerUnit);
+ assert.deepEqual(f.lensWrites, ['second']); assert.deepEqual(f.errors, []); f.controller.destroy();
+});
+
+test('a failed bank load reports its actual error and never falls back to an inaccurate destination', async () => {
+ const f = coldVolumeFixture(), failed = Error('Failed to fetch prepared bank');
+ const restoring = f.controller.restore('https://example.test/sun/?focus=catalogue:a');
+ f.load.reject(failed); await restoring;
+ assert.deepEqual(f.flights, []); assert.equal(f.owner.preparedFocus(),null);
+ assert.deepEqual(f.errors,[failed]); f.controller.destroy();
+});
+
+test('a later saved restore supersedes a pending native focus without a delayed flight', async () => {
+ const f = coldVolumeFixture();
+ const old = f.controller.restore('https://example.test/sun/?focus=catalogue:a');
+ const newer = f.controller.restore('https://example.test/sun/?focus=catalogue:b&v=saved-later-pose');
+ f.load.accept(volumeBank()); await Promise.all([old,newer]);
+ assert.equal(f.owner.preparedFocus()?.id,'catalogue:b');
+ assert.deepEqual(f.flights,[]); assert.deepEqual(f.errors,[]); f.controller.destroy();
+});
+
+
+test('shared context receives the exact authoritative focus on restore, switch and clear', async () => {
+ const f = fixture({ object: { detailedObjectId: 'prepared-image' }, imageLayerFrames: { 'prepared-image': baseFrame } });
+ f.controller.restore(f.windowTarget.location.href);
+ assert.equal(last(f.presentationFocuses), f.owner.preparedFocus());
+ assert.equal(required(last(f.presentationFocuses)).framingRadiusM, 500 * baseFrame.metersPerUnit);
+ const selecting = f.controller.select({ id: 'catalogue:b' });
+ assert.equal(last(f.presentationFocuses), f.owner.preparedFocus());
+ assert.equal(required(last(f.presentationFocuses)).id, 'catalogue:b');
+ f.owner.setPreparedFocus(null);
+ assert.equal(f.presentationFocuses.at(-1), null);
+ f.controller.suspend(); await selecting;
+ f.controller.destroy();
+});
diff --git a/site/test/prepared-focus-card.test.mts b/site/test/prepared-focus-card.test.mts
index 0a5051fa89..2054e7b161 100644
--- a/site/test/prepared-focus-card.test.mts
+++ b/site/test/prepared-focus-card.test.mts
@@ -119,6 +119,20 @@ test('a nebula focus displays its actual classification and preserves the shared
f.card.destroy();
});
+test('a globular-cluster focus keeps its stellar classification and the shared lens controls', () => {
+ const f = fixture();
+ const { membership: _membership, ...common } = f.record;
+ f.card.set({ ...common, kind: 'globular-cluster', detailedObjectId: 'prepared-galaxy',
+ classification: { name: 'Globular cluster', basis: 'Integrated stellar light.', sourceRef: 'observations' } }, [], f.presentation);
+ assert.equal(f.root.querySelector('[data-focus-status]')?.textContent, 'Globular cluster');
+ assert.equal(f.root.querySelector('[data-focus-membership]')?.textContent, 'Milky Way');
+ assert.equal(f.root.querySelector('[data-focus-association]')?.textContent, 'Galactic globular cluster');
+ assert.match(f.root.querySelector('[data-focus-basis]')?.textContent ?? '', /Integrated stellar light/);
+ assert.equal(f.bank.hidden, false);
+ assert.equal(f.datasetTab.hidden, false);
+ f.card.destroy();
+});
+
test('focus uses shared dataset tabs only when a prepared presentation is available', () => {
const f = fixture();
f.card.set(f.record);
diff --git a/src/objects/m31/runtime-assets.json b/src/objects/m31/runtime-assets.json
index 05fc186b5d..631c3bb31c 100644
--- a/src/objects/m31/runtime-assets.json
+++ b/src/objects/m31/runtime-assets.json
@@ -470,7 +470,7 @@
{
"filename": "provenance.json",
"bytes": 38541,
- "sha256": "119f50a4243d3be640ee70232bf9dee9bf056df5c14a444e5d7a21277c7bff2c"
+ "sha256": "d96140b77831ccd093d0da69a5cf4659ddc53d4e11d2c686d8216854a13cdef1"
},
{
"filename": "presentation.json",
diff --git a/src/objects/m33/runtime-assets.json b/src/objects/m33/runtime-assets.json
index bb60145b4f..d3bd8dde30 100644
--- a/src/objects/m33/runtime-assets.json
+++ b/src/objects/m33/runtime-assets.json
@@ -490,7 +490,7 @@
{
"filename": "provenance.json",
"bytes": 37975,
- "sha256": "3af09ece5c85e824edd043a082fdeaf7c3f33bda27b564c4ddb133773ade74cd"
+ "sha256": "2db0a9aa6db465c0eb759ffa694dc4c68f8df98f8e68ac5fb7913e8f0426785d"
},
{
"filename": "presentation.json",
diff --git a/src/objects/nearby-universe/runtime-assets.json b/src/objects/nearby-universe/runtime-assets.json
index 28e3c626bb..a2083df1cb 100644
--- a/src/objects/nearby-universe/runtime-assets.json
+++ b/src/objects/nearby-universe/runtime-assets.json
@@ -15,7 +15,7 @@
{
"filename": "provenance.json",
"bytes": 10354,
- "sha256": "e9233d78dfc14d8a6e633bd740384947be8d72acd07d525dcd6e6d63e5945a51"
+ "sha256": "7ef3613f89ab76ea53800455b138411cdbb57c8725fb5fbf63e1e1a0dd66d308"
},
{
"filename": "presentation.json",
diff --git a/src/objects/nearby-universe/source/manifest.json b/src/objects/nearby-universe/source/manifest.json
index 1e289d46eb..e2a79ebd0d 100644
--- a/src/objects/nearby-universe/source/manifest.json
+++ b/src/objects/nearby-universe/source/manifest.json
@@ -122,8 +122,8 @@
"origin": "project-authored",
"credit": "cssEarth contributors",
"acquisition": "Authored shared navigation frame; only the frame identity and navigation epoch are read. No solar data supplies galaxy positions.",
- "expectedSha256": "02a2fc7561bf3402bdda66f88d21108bc0fca01308eaf864ad108321039b4195",
- "expectedBytes": 3396,
+ "expectedSha256": "84f7b26c703371a694ef560f80bff255ab81585b3d60756e72037f36af8a81af",
+ "expectedBytes": 3395,
"sourceBinding": {
"kind": "local",
"reason": "Authored application frame identity, not a galaxy measurement."
diff --git a/src/objects/omega-centauri/README.md b/src/objects/omega-centauri/README.md
new file mode 100644
index 0000000000..f1ec47dc6c
--- /dev/null
+++ b/src/objects/omega-centauri/README.md
@@ -0,0 +1,205 @@
+# Omega Centauri (NGC 5139)
+
+**493 total retained app elements; visual acceptance remains blocked.** Omega now
+uses 149 XYZ slabs, zero separate stars and one shared geometry bank for both
+optical lenses. Stellar light is baked into the slabs. The new allocation fits
+the inclusive 500-element limit, but still fails the fixed axis-handoff image
+gate and changes delivered brightness. It remains an integration candidate.
+
+## Sources
+
+| Lens | Native image and coverage |
+| --- | --- |
+| [VST/OmegaCAM, eso1119b](https://www.eso.org/public/images/eso1119b/) | G/R/I display composite; 14540 × 14540 pixels; 50.88′ square. Selected geometry/light reference. |
+| [MPG/ESO 2.2-m WFI, eso0844a](https://www.eso.org/public/images/eso0844a/) | B/V/I display composite; 8040 × 7560 pixels; 31.88′ × 29.99′. Same model geometry, different image material. Two mosaic gaps contain publisher-supplied DSS data. |
+
+The [source manifest](source/manifest.json) retains exact image identities, full
+credits and ESO's reuse terms. Stellar light is preserved; no separate star layer
+counts it twice. Published display colors do not establish natural color or
+calibrated luminosity.
+
+The [MGE recipe](source/bake-inputs/references/02-photometric-mge.json) uses the eight
+projected Gaussians in [D’Souza & Rix (2013), Table 1](https://doi.org/10.1093/mnras/sts426).
+The selected inclination is 50° and photometric major-axis PA 100° east of north.
+The [5426 ± 47 pc distance](https://doi.org/10.1093/mnras/stab1474) is the combined
+NGC 5139 distance in Baumgardt & Vasiliev (2021), Table 2. This locates the cluster,
+not individual stars. Competing fits and the rejected spherical King-Abel
+experiment remain in the [physical evidence](source/bake-inputs/references/04-physical-evidence.json).
+
+## Current 500-element bake
+
+The selected result is `59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e`.
+Its [delivery recipe](source/delivery.json) records the failed visual qualification;
+`acceptedLabResult` identifies this candidate, not an acceptance certificate.
+
+- **Budget:** the compiler reserves stars, renderer copies and overhead before
+ allocating XYZ slabs. It planned 151 slabs and removed two fully transparent
+ slabs, retaining **X 48, Y 45, Z 56**. All hidden nodes count. The app retains
+ **447 slab elements + 26 impostors + 20 wrappers = 493**, down from 1,423.
+ RGB lens switches reuse the same topology. The actual Lab retains 462 elements.
+ [Bake and validation record](evidence/2026-09-21/element-budget/validation.json).
+- **Preserved light:** all 4,085 fitted emission features, the photometric
+ envelope, registration and optical materials are unchanged. Zero separate
+ stellar points were present before or after; no stellar light was silently
+ dropped or counted twice. The source-owned [compact input](source/bake-inputs.json.gz)
+ is 2,229,420 bytes, SHA-256
+ `c9f2535b13f4b4745179ec53b63240df1207ef7f38a4ea34dff76cba50a74d68`.
+- **Replay:** the ordinary application command ran without the Lab, caches or
+ network in 232.93 seconds. It reproduced all 447 expected raw PNGs and 59
+ delivery texture/bank files exactly. The current final receipt admits 493
+ elements. [Cold replay](evidence/2026-09-21/element-budget/cold-replay.json).
+- **Lab behavior:** the real Reconstruction route loads this result, keeps 462
+ elements through both lenses, orbit and zoom, and restores the same result and
+ camera after refresh. No processing requests or browser errors occurred.
+ [Actual Lab check](evidence/2026-09-21/element-budget/lab-browser.json).
+- **App behavior and delivery:** actual native arrival, orbit, close zoom,
+ galaxy-context return and both lens switches each retain exactly 493 elements.
+ Cold and warm framing remain 204.5516 pixels; one scene/camera and no browser
+ errors are recorded. [Native check](evidence/2026-09-21/element-budget/native-focus.json)
+ and [seven controlled views](evidence/2026-09-21/element-budget/app-inspection.json).
+ All 64 published files passed [cold R2 restoration and network-free reuse](evidence/2026-09-21/element-budget/r2-restore.json).
+- **Approximation tradeoff:** all 1,040 reference depth cells remain integrated,
+ but their positions are represented by fewer slabs. The planner explicitly
+ reports `budget-limited`: estimated axis errors are 10.5–10.7% against its 1%
+ target. Measured front views are 23–25% brighter than the previous 459-slab
+ bank. The [matched comparison](evidence/2026-09-21/element-budget/pixelmatch.json)
+ retains equal-size inputs and Pixelmatch diffs at threshold 0.1.
+- **Visual blocker:** neutral X/Z and Y/Z handoff L1 is **0.131881 / 0.125192**,
+ above the unchanged **0.04** limit. All nine tested lens/axis pairs fail that
+ image-stability limit; brightness disagreement between axes stays below 5%.
+ [Fixed-camera handoffs](evidence/2026-09-21/element-budget/handoffs.json).
+
+The [descriptor](object.json), [prepared inventory](prepared-assets.json) and
+[runtime inventory](runtime-assets.json) pin the current delivery. Earlier fit
+metrics below still describe its identical field; older captures, byte counts
+and performance timings describe their explicitly dated earlier banks.
+
+The production build, 596 renderer tests, 29 source/package checks, 22 maintained
+CI-entry checks and strict types pass. The complete Lab sweep has 664 passes,
+17 skips and five failures from missing LMC research assets or temporary fixture
+package resolution; [the transcript](evidence/2026-09-21/element-budget/lab-suite.tap)
+is retained. This is not a full-suite pass. [Independent review and mutation evidence](evidence/2026-09-21/element-budget/review/verified.md)
+cover total accounting and rejection before package replacement.
+
+## Earlier integration evidence
+
+- The earlier selected result was `7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549`.
+ Its [delivery recipe](source/delivery.json) explicitly records
+ `app-integration-candidate` and failed visual handoff. The legacy field
+ `acceptedLabResult` identifies this selected source; it is not an acceptance
+ certificate.
+- The earlier 2,277,027-byte compact input, SHA-256
+ `869e9ab24b644c4ea7d04cf2d4e2c719445c205b879eaebf13a420c7d7d75eac`,
+ retains the emission field, component materials and adaptive sampling.
+ [Cold application replay](evidence/2026-09-20/cold-replay.json) reproduced all
+ 1,377 expected raw resource identities across neutral and two optical banks,
+ then matched all 60 prepared delivery files in 218.60 seconds. It started
+ without the local cache, Omega prepared files or staging outputs.
+- That earlier delivery had 459 retained slabs per optical lens, 29 packed
+ resources and zero separate stars, plus two separate dataset previews.
+ These file checks do not qualify the rendered appearance.
+- The generic application discovery functions admit one destination at
+ `/sun/?focus=omega-centauri`, classified as a Galactic globular cluster.
+ [Controlled-camera browser automation](evidence/2026-09-20/app-inspection.json)
+ captured both lenses, front/oblique and both side axes, with seven captures, no
+ page errors or failed requests, one mounted scene/camera and no canvas. It applies
+ inspection camera poses after mounting; those captures alone do not prove native
+ arrival or pass the visual handoff gate.
+- The separate [native navigation check](evidence/2026-09-20/native-focus.json)
+ verifies automatic arrival on both cold and warm visits at a projected radius
+ of **204.5516 pixels** in a 1440 × 1000 viewport, without injected inspection
+ poses. Real drag and wheel inputs changed orientation and distance; reselection
+ restored the arrival framing. No page errors or failed requests were recorded.
+ Only Omega's volume bank mounts at cold arrival; the Milky Way overview is
+ hidden. Real wheel input out to 100 framing radii restores the surrounding
+ models, and reselection hides them again. The shared presentation fade is
+ zero through eight radii and fully restored by 32; camera coordinates and the
+ Sun-distance sky contribution are preserved. These checks close the arrival
+ and context-visibility defects, not the visual limits below.
+- Three [native performance baselines](evidence/2026-09-20/performance-baseline.json)
+ reproduced slow dragging. A [page-only visibility control](evidence/2026-09-20/performance-background-control.json)
+ reduced the identical 40-step drag from 33.06 to 8.38 seconds by hiding the
+ other detailed banks. Chromium used SwiftShader software rendering: these
+ measurements isolate background rendering cost, not desktop GPU frame rate.
+- Three [actual-policy repeats](evidence/2026-09-20/performance-after.json), with
+ no visibility override, took **8.11–8.19 seconds**, versus **32.17–32.70 seconds**
+ before the fix. [The comparison](evidence/2026-09-20/performance-comparison.json)
+ verifies identical starting and ending camera poses: median drag time fell
+ 74.79%, and cold DOM residency fell from 29,063 to 18,068 elements. SwiftShader
+ remains the measurement environment. [Regression and mutation checks](evidence/2026-09-20/context-regressions.json)
+ cover fetching, painting, selected-bank preservation and sky composition.
+- [R2 restoration](evidence/2026-09-20/r2-restore.json) fetched and hash-verified all
+ 64 pinned assets into an empty target; an immediate repeat reused all 64 with
+ no downloads. This proves the pinned published bytes, not scientific accuracy.
+- The final build, 594 renderer tests and 29 source/provenance tests pass.
+ The [broad shell run](evidence/2026-09-20/shell-tests.tap) passes 376 of 399
+ tests; 23 fail on other object data, legacy fixtures or test-loader setup.
+ Those failures were not independently reproduced on current main. The
+ [validation record](evidence/2026-09-20/validation.json) separates this run
+ from the passing native-navigation and context regressions.
+- [Retained Lab measurements and views](../../../labs/nebula/models/omega-centauri/evidence/2026-09-20/README.md)
+ identify the tested candidate. Its neutral X/Z and Y/Z normalized L1 values are
+ **0.124987 and 0.114655**, above the fixed 0.04 limit. Both optical lenses also
+ fail that image-stability gate. Neutral X/Y additionally exceeds the 0.05
+ brightness limit. The optimizer's coarse target result is not a visual certificate.
+
+Local application preparation ran on 2026-09-20 using code at
+`862cdbed96bbca43cdf1f13549c94e4ca501add3` plus the uncommitted Omega object package
+and scoped provenance selector; native navigation was checked with the subsequent
+uncommitted shared focus and context-visibility fixes. The exact input and output inventories above
+identify that candidate; the commit alone does not contain its app package. The
+[retained validation record](evidence/2026-09-20/validation.json) binds these local
+checks and the [native arrival](evidence/2026-09-20/native-arrival.png),
+[front](evidence/2026-09-20/front.png),
+[oblique](evidence/2026-09-20/oblique.png),
+[X-side](evidence/2026-09-20/side-x.png),
+[Y-side](evidence/2026-09-20/side-y.png) and
+[WFI](evidence/2026-09-20/wfi.png) views to that working-tree state.
+[Replay references](source/replay-references.json) resolve every retained research
+input through an application-owned copy. [Provenance references](source/provenance-references.json)
+pin those copies to the research files at that commit. No current Lab-file reads
+are required for compact replay.
+
+The [2026-09-21 merge check](evidence/2026-09-21/merge-validation.json) records
+integration with main at `3aebc31094dff8332755b394b88c708b7c3f1062`. A fresh
+[native navigation run](evidence/2026-09-21/native-focus.json) preserves the
+204.5516-pixel cold/warm framing and close-up/background restoration behavior.
+The model, textures and recorded context-owner pins are unchanged; the six views
+and performance measurements above remain the explicitly dated earlier evidence.
+
+## Known problems
+
+- Desktop GPU performance has not been measured. The headless visibility control
+ shows a substantial background cost, but does not establish a desktop frame rate.
+- The shared zoom minimum remains **0.05 framing radii (2.17 pc)**. The wheel
+ check reached that limit; it does not establish an acceptable close-up view.
+ This is separate from the corrected automatic arrival framing.
+- Axis-handoff image stability remains outside the unchanged acceptance limits.
+ Earlier Lab inspection also exposed a false grid and an oblique compositor
+ rectangle; successful app preparation does not close those renderer findings.
+- Near/far tilt and the six-sigma numerical support cutoff are authored. The
+ MGE's coefficient uncertainties are unavailable; modern kinematic inclination
+ cannot be substituted without checking mathematical admissibility.
+- Absolute astrometry starts from publisher AVM. Relative image registration
+ does not establish absolute Gaia astrometry or independent WFI detections in
+ DSS-filled gaps.
+- The 512-pixel fit and minimum 5.80-arcsec projected sigma soften native stellar
+ texture. Crowding, saturated cores, seeing and publisher display stretches remain.
+ Fitted light features are not a resolved-star catalogue, dynamical mass map or
+ N-body simulation.
+
+## Method and earlier trials
+
+A smooth oblate MGE envelope carries broad integrated starlight; finite positive
+image residuals receive conditional depths and lens-specific materials. Both
+optical lenses use the same geometry. The
+[method account](../../../labs/nebula/models/omega-centauri/README.md) explains the
+numerical choices and retains earlier trials: bake 1 was rejected for bright
+clumps, bake 2 stopped during material painting, and bake 3 completed after the
+roundoff correction but retained the failed neutral handoff. Adaptive grouping
+reduced the 779 retained slabs to the earlier 459-slab candidate without passing
+that gate. The current total-element allocation reduces it further to 149 slabs.
+
+The [investigation ledger](investigations.json) records selected sources,
+alternatives and unresolved acceptance. Shared preparation and replay commands
+belong in the [nebula guide](../../../docs/nebulae/README.md).
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/app-inspection.json b/src/objects/omega-centauri/evidence/2026-09-20/app-inspection.json
new file mode 100644
index 0000000000..b01d35aef3
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/app-inspection.json
@@ -0,0 +1,166 @@
+{
+ "objectId": "omega-centauri",
+ "base": "http://127.0.0.1:4219",
+ "browser": "148.0.7778.96",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "deviceScaleFactor": 1,
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "dirtyFiles": [
+ "M .gitignore",
+ " M docs/provenance/investigation-index.md",
+ " M site/prepared-context-navigation.mts",
+ " M site/test/prepared-context-navigation.test.mts",
+ " M src/objects/m31/runtime-assets.json",
+ " M src/objects/m33/runtime-assets.json",
+ " M src/objects/nearby-universe/object.json",
+ " M src/objects/nearby-universe/prepared/manifest.json",
+ " M src/objects/nearby-universe/runtime-assets.json",
+ " M src/renderers/css/sky/prepared-sky-runtime.test.ts",
+ " M src/renderers/css/universe/prepared-universe-runtime.ts",
+ " M tests/objects/browser/volume-lens-bank/promotion-inspection.mts",
+ " M tools/prepare-volume-provenance.mts",
+ " M tools/prepare-volume-provenance.test.mts",
+ "?? src/objects/omega-centauri/",
+ "?? src/renderers/css/universe/detailed-focus-context.test.ts",
+ "?? src/renderers/css/universe/detailed-focus-context.ts",
+ "?? src/sources/baumgardt-vasiliev-2021.json",
+ "?? src/sources/dsouza-rix-2013.json",
+ "?? src/sources/eso-eso0844a.json",
+ "?? src/sources/eso-eso1119b.json",
+ "?? tests/objects/browser/omega-centauri/"
+ ],
+ "inputPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/provenance.json",
+ "bytes": 76935,
+ "sha256": "b06370b5aeecd477765f16c6e77051e2b5d85a6bad3b5360ab1528b5cc65355f"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/presentation.json",
+ "bytes": 4200,
+ "sha256": "69e809ee3e10109b51e9e3a2e8f159436a7038a43693e8820da396a90e0c53cc"
+ }
+ ],
+ "navigation": "direct native /sun/?focus= route",
+ "lenses": [
+ {
+ "id": "eso1119b",
+ "label": "VST · optical G / R / I",
+ "textures": 5,
+ "cameraRetained": true,
+ "sourceIds": [
+ "eso-eso1119b"
+ ]
+ },
+ {
+ "id": "eso0844a",
+ "label": "WFI · optical B / V / I",
+ "textures": 1,
+ "cameraRetained": true,
+ "sourceIds": [
+ "eso-eso0844a"
+ ]
+ }
+ ],
+ "stars": {
+ "total": 0,
+ "visibleWithStarsOn": 0,
+ "rootDisplayOn": "",
+ "rootDisplayOff": ""
+ },
+ "captures": [
+ {
+ "name": "lens-eso1119b",
+ "path": "output/pr390-audit/app-integration/browser-context-final/lens-eso1119b.png",
+ "bytes": 361535,
+ "sha256": "69ef8f6d11596069359187e996fdb716421edd23bac7a46972f5dcec9d949ded",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.369697426395,-0.684818648265,0.627970725357,0,0.929152201158,0.272480322918,-0.249861282927,0,0,0.675853454982,0.737036028556,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.017370865949,0.767816489802,-0.640434129591,0,0.885889099856,-0.28515750824,-0.365903839695,0,-0.463571706821,-0.573709677337,-0.675246952839,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.6462637826065765,\"distance\":69961698138109.18,\"distanceKilometers\":157007591595750180,\"distanceRadii\":225682897219.70703,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":0,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":179.99153509804657,\"silhouetteRadius\":0,\"bodyCenterKilometers\":[10329707871598.312,-20769413064916.992,157007589882229860]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNCosolIvrcoMKy48F86NT-Q4FubJGvw6NBQsczQAAAAL_TNG-ckHq_v8oOQfBFK1i_4L3efI6XFgABAAAAAAAAAAA",
+ "view": "earth",
+ "lens": "eso1119b",
+ "textures": 5
+ },
+ {
+ "name": "lens-eso0844a",
+ "path": "output/pr390-audit/app-integration/browser-context-final/lens-eso0844a.png",
+ "bytes": 375193,
+ "sha256": "b0c6a4e887c73e704a96d9d54846882f0c37699657a8393ba5231258bb33e7c7",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.369697426395,-0.684818648265,0.627970725357,0,0.929152201158,0.272480322918,-0.249861282927,0,0,0.675853454982,0.737036028556,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.017370865949,0.767816489802,-0.640434129591,0,0.885889099856,-0.28515750824,-0.365903839695,0,-0.463571706821,-0.573709677337,-0.675246952839,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.6462637826065765,\"distance\":69961698138109.18,\"distanceKilometers\":157007591595750180,\"distanceRadii\":225682897219.70703,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":0,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":179.99153509804657,\"silhouetteRadius\":0,\"bodyCenterKilometers\":[10329707871598.312,-20769413064916.992,157007589882229860]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso0844a&v=UMNCosolIvrcoMKy48F86NT-Q4FubJGvw6NBQsczQAAAAL_TNG-ckHq_v8oOQfBFK1i_4L3efI6XFgABAAAAAAAAAAA",
+ "view": "earth",
+ "lens": "eso0844a",
+ "textures": 1
+ },
+ {
+ "name": "view-earth",
+ "path": "output/pr390-audit/app-integration/browser-context-final/view-earth.png",
+ "bytes": 361515,
+ "sha256": "15699f9c5573f4b9270b33f12c0ecce680dc8e1cb4a42f363973e145b5d2f595",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.369697426395,-0.684818648265,0.627970725357,0,0.929152201158,0.272480322918,-0.249861282927,0,0,0.675853454982,0.737036028556,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.017370865949,0.767816489802,-0.640434129591,0,0.885889099856,-0.28515750824,-0.365903839695,0,-0.463571706821,-0.573709677337,-0.675246952839,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.6462637826065765,\"distance\":69961698138109.18,\"distanceKilometers\":157007591595750180,\"distanceRadii\":225682897219.70703,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":0,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":179.99153509804657,\"silhouetteRadius\":0,\"bodyCenterKilometers\":[10329707871598.312,-20769413064916.992,157007589882229860]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNCosolIvrcoMKy48F86NT-Q4FubJGvw6NBQsczQAAAAL_TNG-ckHq_v8oOQfBFK1i_4L3efI6XFgABAAAAAAAAAAA",
+ "view": "earth (0°)",
+ "lens": "eso1119b",
+ "textures": 1
+ },
+ {
+ "name": "view-oblique",
+ "path": "output/pr390-audit/app-integration/browser-context-final/view-oblique.png",
+ "bytes": 385925,
+ "sha256": "e22fbcb65d767e56927a30ab214caad06764a195cc0650845a25f9cc469fa253",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.659703396329,-0.684818648265,0.30953973551,0,0.62578011049,0.272480322918,-0.730858212609,0,0.416161846145,0.675853454982,0.608302083839,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.375953105308,0.767816489802,-0.51876476744,0,0.524550975902,-0.28515750824,-0.802204083029,0,-0.763875349308,-0.573709677337,-0.295553083902,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.39642396816377173,\"distance\":68455670452062.64,\"distanceKilometers\":153627773979032200,\"distanceRadii\":220824743393.75046,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":0,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":142.08246869895342,\"silhouetteRadius\":0,\"bodyCenterKilometers\":[94408353542680690,-20769413064933.375,121196350142008350]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNDdPZ-W4vbB8Ky48F86OVgQ3rpNwNknMJBQsczQAAAAL_cPfj3mMPjP6Ef-4phSgq_2k_88qH1IAABAAAAAAAAAAA",
+ "view": "oblique (34°)",
+ "lens": "eso1119b",
+ "textures": 1
+ },
+ {
+ "name": "view-side-plus",
+ "path": "output/pr390-audit/app-integration/browser-context-final/view-side-plus.png",
+ "bytes": 409805,
+ "sha256": "526cf13e89988b5d33392abd97db842ade6fea989a7c74e665b219481bb02bca",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.627970725357,-0.684818648265,-0.369697426395,0,-0.249861282927,0.272480322918,-0.929152201158,0,0.737036028556,0.675853454982,0,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.640434129591,0.767816489802,0.017370865949,0,-0.365903839695,-0.28515750824,-0.885889099856,0,-0.675246952839,-0.573709677337,0.463571706821,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.4533237230626572,\"distance\":74790239926811.3,\"distanceKilometers\":167843773926072960,\"distanceRadii\":241258838473.58484,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":1.1681049271654376e-7,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":84.92249396653635,\"silhouetteRadius\":5.840524635827188e-8,\"bodyCenterKilometers\":[167185134868234850,-20769413064916.992,-14854719568820442]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNDgo-v9HxgE8Ky48F86NT-w0pjJPWRnG1BQsczQAAAAL_ioMoqOXy5P9mwpgtBTDe_xDFgwHL_2QABAAAAAAAAAAA",
+ "view": "exact +90° side",
+ "lens": "eso1119b",
+ "textures": 2
+ },
+ {
+ "name": "view-side-minus",
+ "path": "output/pr390-audit/app-integration/browser-context-final/view-side-minus.png",
+ "bytes": 327428,
+ "sha256": "bf2948d6d976446c77e0c4e77fb941af94ae31bd4d40f9b73668555e5bf32333",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(-0.627970725357,-0.684818648265,0.369697426395,0,0.249861282927,0.272480322918,0.929152201158,0,-0.737036028556,0.675853454982,0,0,0,0,0,1)\",\"skybox\":\"matrix3d(0.640434129591,0.767816489802,-0.017370865949,0,0.365903839695,-0.28515750824,0.885889099856,0,0.675246952839,-0.573709677337,-0.463571706821,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.45395489611826695,\"distance\":74789425752856.11,\"distanceKilometers\":167841946762135460,\"distanceRadii\":241256212105.98746,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":1.1697317484856256e-7,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":84.92951874427573,\"silhouetteRadius\":5.848658742428128e-8,\"bodyCenterKilometers\":[-167185134868234850,-20769413064933.375,-14834060153077260]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UEPDgo-v9HxgE8Ky48F86OVgw0pZv-MAHwZBQsczQAAAAL_EMWDAcv_YP-Kgyio5fLi_2bCmC0FMNQABAAAAAAAAAAA",
+ "view": "exact −90° side",
+ "lens": "eso1119b",
+ "textures": 2
+ },
+ {
+ "name": "view-side-y",
+ "path": "output/pr390-audit/app-integration/browser-context-final/view-side-y.png",
+ "bytes": 339301,
+ "sha256": "b76500bd18ebbec4020b392f4129ea5d5b73aa4b9926e12cbf44c4f5c12d0640",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.369697426395,-0.627970725357,-0.684818648265,0,0.929152201158,0.249861282927,0.272480322918,0,0,-0.737036028556,0.675853454982,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.017370865949,0.640434129591,0.767816489802,0,0.885889099856,0.365903839695,-0.28515750824,0,-0.463571706821,0.675246952839,-0.573709677337,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.535181395928246,\"distance\":74707421314841.7,\"distanceKilometers\":167657912931404400,\"distanceRadii\":240991681660.7797,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":1.3791044440937067e-7,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":85.6961589587144,\"silhouetteRadius\":6.895522220468534e-8,\"bodyCenterKilometers\":[10329707871615.2,-167185134868234850,-12581984775926776]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNCosolIvr-ZsOCj6_0fGATw0ZZn86RufxBQsczQAAAAD_VUn7dgmC8P8ztrxvw-8q_4HG2k1oDJQABAAAAAAAAAAA",
+ "view": "exact local Y-axis side (90° about X)",
+ "lens": "eso1119b",
+ "textures": 3
+ }
+ ],
+ "errors": [],
+ "failed": [],
+ "result": "passed"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/cold-replay.json b/src/objects/omega-centauri/evidence/2026-09-20/cold-replay.json
new file mode 100644
index 0000000000..52b76af13a
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/cold-replay.json
@@ -0,0 +1,471 @@
+{
+ "status": "verified",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "verifiedAt": "2026-09-20T20:53:01.471Z",
+ "execution": {
+ "startedAt": "2026-09-20T20:49:08.660Z",
+ "endedAt": "2026-09-20T20:52:47.266Z",
+ "durationSeconds": 218.601808708,
+ "exitCode": 0,
+ "sharp": {
+ "aom": "3.14.1",
+ "archive": "3.8.8",
+ "cairo": "1.18.4",
+ "cgif": "0.5.3",
+ "exif": "0.6.26",
+ "expat": "2.8.2",
+ "ffi": "3.6.0",
+ "fontconfig": "2.18.1",
+ "freetype": "2.14.3",
+ "fribidi": "1.0.16",
+ "glib": "2.89.1",
+ "harfbuzz": "14.2.1",
+ "heif": "1.23.1",
+ "highway": "1.4.0",
+ "imagequant": "2.4.1",
+ "lcms": "2.19.1",
+ "mozjpeg": "0826579",
+ "pango": "1.58.0",
+ "pixman": "0.46.4",
+ "png": "1.6.58",
+ "proxy-libintl": "0.5",
+ "rsvg": "2.62.90",
+ "tiff": "d01a94b",
+ "uhdr": "1acdbed",
+ "vips": "8.18.3",
+ "webp": "1.6.0",
+ "xml2": "2.15.3",
+ "zlib-ng": "2.3.3",
+ "sharp": "0.35.3"
+ }
+ },
+ "expectedSlabs": {
+ "neutral": 459,
+ "eso1119b": 459,
+ "eso0844a": 459
+ },
+ "verifiedExpectedSlabs": 1377,
+ "verifiedArtifacts": 60,
+ "artifacts": [
+ {
+ "filename": "delivery.json",
+ "bytes": 576,
+ "sha256": "f1753dcbd3c1d5d72633cccbb6bc69f61d60ca0378e6147aee8b91f04c891792"
+ },
+ {
+ "filename": "eso0844a/atlases/x.webp",
+ "bytes": 1590702,
+ "sha256": "f552913f7a8bef2d5ed213070996662a18065b969014d77d29e3dd8475718abb"
+ },
+ {
+ "filename": "eso0844a/atlases/y.webp",
+ "bytes": 1563374,
+ "sha256": "f8961a5c528dacc1a2f502990054e8d3af168b3b8bf7f9f48a45e7521b40137f"
+ },
+ {
+ "filename": "eso0844a/atlases/z.webp",
+ "bytes": 1578030,
+ "sha256": "398566ba9d6e1c0397560b042d02a02874132f08b0bfbbb8e49bf8729c4e7dfa"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00n.png",
+ "bytes": 12781,
+ "sha256": "3f30cb4a6b7bcd0eb2d629af30b01a25aede20460da48480704e8c1db5ef3759"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00p.png",
+ "bytes": 12814,
+ "sha256": "3e2914737aa86ff78b5e341bb947e6747fe69a6743d330b5221beffb1a80e668"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0n0.png",
+ "bytes": 14309,
+ "sha256": "c71c68ade95973a89bf755a981e504328e4049fd89123f611263b13ea265db2c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0nn.png",
+ "bytes": 16281,
+ "sha256": "7b727ab20e297e046c9c2b731ea947caab6ea16dd7c87d3af0e3f8119f5ee1a1"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0np.png",
+ "bytes": 16645,
+ "sha256": "d1917707b737fa9f09e0d55289b7d508e400610d2dead5296d45c7e5e02549b5"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0p0.png",
+ "bytes": 14278,
+ "sha256": "e604cd6936356ad8cebbfd4dee3e83e24fdf8343f1a22e0d96c6ccf086bb2c0b"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pn.png",
+ "bytes": 16603,
+ "sha256": "4e3817325bbb393ce73e0297f308bcff87251572f052cad6f68d5fcdaf0bf471"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pp.png",
+ "bytes": 16276,
+ "sha256": "92a50de658df291184dfd54da8bab0c6ea2a261d732da098c430873649b4e909"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n00.png",
+ "bytes": 13824,
+ "sha256": "312b5c2d0359368cce0c1cdaaff3953daad28ad476d37a9de7e4658d157be60d"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0n.png",
+ "bytes": 16267,
+ "sha256": "eba159fb949f749e0dcc84055605e056a5b1ca5456fd553eb5099c664d4e28ea"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0p.png",
+ "bytes": 16163,
+ "sha256": "9654c8617ea1a3ac39d554be1f4ffa6c13127f102201a2b2a1bb6e83c210d094"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nn0.png",
+ "bytes": 16946,
+ "sha256": "0f087f5b96fabe22db213eddf59d054cb70d280907bea79e53a203da2c9b8674"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnn.png",
+ "bytes": 18279,
+ "sha256": "a4268964eb18e91f52cfddf34cd93c3b12bc515d7c237678548a17e99d5f132e"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnp.png",
+ "bytes": 17927,
+ "sha256": "e23523b8a86bf2dfecef40a63321f186267c4cfef0bb7f5cd50a418a4fd7a09f"
+ },
+ {
+ "filename": "eso0844a/impostors/view-np0.png",
+ "bytes": 16400,
+ "sha256": "a5ad7416d1419bb7d91f23281a87283133d949754f42d65f72794ca48dc68dc6"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npn.png",
+ "bytes": 18052,
+ "sha256": "34ab6a989fee1ae995340e32377f61eb03d6d74a292f55d1b13fc6e2d86dfb06"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npp.png",
+ "bytes": 17807,
+ "sha256": "a4fba09da43abedace04a62257ad5e4438f7d3b9aabf349350ca6ba5422350d3"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p00.png",
+ "bytes": 13847,
+ "sha256": "ac1c8bb9baced6508068979a00885377abe5dd42679d55745ebbb8b69a90196f"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0n.png",
+ "bytes": 16103,
+ "sha256": "b1bd23ec644909906e35d1cf0cbf694c57396c27692d4f44a6be6b2b241200d9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0p.png",
+ "bytes": 16256,
+ "sha256": "230f731c284d627a242d5fd32ac2ac7b5243b7960c8e315c9bc8fa502675ceb5"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pn0.png",
+ "bytes": 16398,
+ "sha256": "23a9b3ab916bb65d80911319b1ddb9f6a18388d4ac9da49c75447d11afd3ffaf"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnn.png",
+ "bytes": 17852,
+ "sha256": "0647793e2f7308e5d963dd347db7342d658502484d6f2e53d88d95977100171d"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnp.png",
+ "bytes": 18068,
+ "sha256": "a088f38868a3c77048496748bee8a612e4ef1850ef252a5e4c4acd0d83242359"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pp0.png",
+ "bytes": 16994,
+ "sha256": "417ee690667fed688113ecfdf989e78039b1db4c2a75e133b34608ec2ad6fbb4"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppn.png",
+ "bytes": 17978,
+ "sha256": "7ff6ca4aa06054cc1170da2d1d17949deca83bb031e34119a43f7fb4a93c5df4"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppp.png",
+ "bytes": 18270,
+ "sha256": "f327ebf876775e779fbf4b7b5ace627f85f860b0a8f80cd16159fb593c0c849b"
+ },
+ {
+ "filename": "eso1119b/atlases/x.webp",
+ "bytes": 1781008,
+ "sha256": "b7e693615f2de5972e15e7ee2d703ec1a09ad73fed060c3cd303179cee2f3262"
+ },
+ {
+ "filename": "eso1119b/atlases/y.webp",
+ "bytes": 1725652,
+ "sha256": "f933ddebe0c18cbaf393d973a4c32fbcf0cae5e044dd585772611173329934eb"
+ },
+ {
+ "filename": "eso1119b/atlases/z.webp",
+ "bytes": 1837078,
+ "sha256": "a327f9be666bcec8b5c5b5778e0069cc75a9862ba5dd3ccc74aaa26d688eb16d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00n.png",
+ "bytes": 17775,
+ "sha256": "f2674a9bf843dfd253c3a3e13297ace857da8e5b2e4b4426286956ab296e3903"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00p.png",
+ "bytes": 17774,
+ "sha256": "3eed839f18fec65fb2b9655061fb9e27a4dee27956a642d9c04b4e49693b266a"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0n0.png",
+ "bytes": 18208,
+ "sha256": "fe2a4e3ca81034c7edd80973f71bf7dea2de68ec49e67b4619a2340e514e0865"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0nn.png",
+ "bytes": 20994,
+ "sha256": "d28ea496188c619de7051ee4587e9b1a791062a6429e47c2737260e445420e31"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0np.png",
+ "bytes": 21852,
+ "sha256": "37b9bd534fab36690564a1bf913e79a562300dfbf13c04d23da6f898d2298fcf"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0p0.png",
+ "bytes": 18165,
+ "sha256": "e7248727f96a3c77c0ed6cef064caa2c8fd58a53263a81cb9ff82761f3005588"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pn.png",
+ "bytes": 21853,
+ "sha256": "70e6bce0f8f202091242a96353d9ce15906c9c0e1b132e8fce9349d3a99872b4"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pp.png",
+ "bytes": 21017,
+ "sha256": "5323bfa51cbd316c49b5465a320c503b7bafb1c7e860af3f99563df0cca8d064"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n00.png",
+ "bytes": 18408,
+ "sha256": "0d12581cd70d9c6e8a405c32e59cf583fed79c2de31415a67d53db67647086cf"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0n.png",
+ "bytes": 21522,
+ "sha256": "2ffbecc2d78407fe8082780eae1766bd64c5fd80a472e090a9d61f3e42fa62c7"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0p.png",
+ "bytes": 21049,
+ "sha256": "c935d2479ca0888c434570561a51e4f82cf4900aebe0509704b9c5aab744c5fc"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nn0.png",
+ "bytes": 20581,
+ "sha256": "6da2999fb914f092896d3092345db2a7bb15d772ba89f245f6840b62d89717f5"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnn.png",
+ "bytes": 23128,
+ "sha256": "3a51d561f71a67f5e504550ca604b2595d289e5face6f7e6b411d5018cb39b54"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnp.png",
+ "bytes": 23076,
+ "sha256": "2b267105d2a959e4a7b766baaf3d0d2d3bd62f29b0e29d12f0e30a5a0a1fce39"
+ },
+ {
+ "filename": "eso1119b/impostors/view-np0.png",
+ "bytes": 20496,
+ "sha256": "4d5ff2dc57d9acf495f1e4f1a0dac2b2d1b6b477b87859ce0cd1a880626e3ea7"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npn.png",
+ "bytes": 23747,
+ "sha256": "b20d7118a10509f20ca8c6daac26a03545eab0430ccea9361d0c072a1e012bf6"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npp.png",
+ "bytes": 22734,
+ "sha256": "fc5d3fffd9ad8a1bbff9d858204eb791b7862eb7817532a1c86d59f84dd1c6eb"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p00.png",
+ "bytes": 18325,
+ "sha256": "4304a8f8567dcd622e952c4b8b2a5de79b99d21b8f0538f1bd8ba0b44fb10220"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0n.png",
+ "bytes": 21035,
+ "sha256": "38158cc27df1935088f4b78ff999bebef983737cf9817eab49f3c3b4f61c451c"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0p.png",
+ "bytes": 21472,
+ "sha256": "b87d11c99416f0353c4f93c75c6f33315dd70a12bd4a2ad429574e4ff2084b18"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pn0.png",
+ "bytes": 20452,
+ "sha256": "20250aae317317ca1bc6ac9418f3921e151e8a63587ebd38d3d3ea6b4467e06d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnn.png",
+ "bytes": 22755,
+ "sha256": "a5c284e57bebf454e0aa7cffa96f77319f1e001caa6b1d008189509776b4686d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnp.png",
+ "bytes": 23735,
+ "sha256": "8190a7636d3454778ee1faa611af94d930e3bfcd1d5a988e718f31a172cfaf34"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pp0.png",
+ "bytes": 20611,
+ "sha256": "de3b4315a88952ae1321715d9b249adf7b9e548c4742bf195e9a51926bd5f09e"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppn.png",
+ "bytes": 23084,
+ "sha256": "dcc980e099d2f59298ec55ec82c87040d9b4b21967d85274fde2788e16cf9dfa"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppp.png",
+ "bytes": 23136,
+ "sha256": "8744fe9f974da4598ee0c1300fc2d3cbdc330337e0f186231bc5e37c73d55781"
+ },
+ {
+ "filename": "lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ }
+ ],
+ "rootInventoryExtras": [
+ {
+ "filename": "presentation.json",
+ "bytes": 4200,
+ "sha256": "69e809ee3e10109b51e9e3a2e8f159436a7038a43693e8820da396a90e0c53cc"
+ },
+ {
+ "filename": "provenance.json",
+ "bytes": 76935,
+ "sha256": "b06370b5aeecd477765f16c6e77051e2b5d85a6bad3b5360ab1528b5cc65355f"
+ }
+ ],
+ "lensCounts": [
+ {
+ "id": "eso1119b",
+ "axes": [
+ {
+ "axis": "x",
+ "layers": 146
+ },
+ {
+ "axis": "y",
+ "layers": 142
+ },
+ {
+ "axis": "z",
+ "layers": 171
+ }
+ ],
+ "layers": 459,
+ "stars": 0
+ },
+ {
+ "id": "eso0844a",
+ "axes": [
+ {
+ "axis": "x",
+ "layers": 146
+ },
+ {
+ "axis": "y",
+ "layers": 142
+ },
+ {
+ "axis": "z",
+ "layers": 171
+ }
+ ],
+ "layers": 459,
+ "stars": 0
+ }
+ ],
+ "preparedDescriptorSha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813",
+ "sourceInputsUnchanged": true,
+ "sourcePins": {
+ "source/bake-inputs/references/01-compiler.json": {
+ "bytes": 1361,
+ "sha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f"
+ },
+ "source/bake-inputs/references/02-photometric-mge.json": {
+ "bytes": 2508,
+ "sha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914"
+ },
+ "source/bake-inputs/references/03-observation-structures.json": {
+ "bytes": 397,
+ "sha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e"
+ },
+ "source/bake-inputs/references/04-physical-evidence.json": {
+ "bytes": 36711,
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51"
+ },
+ "source/bake-inputs/references/05-observations.json": {
+ "bytes": 3842,
+ "sha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2"
+ },
+ "source/bake-inputs.json.gz": {
+ "bytes": 2277027,
+ "sha256": "869e9ab24b644c4ea7d04cf2d4e2c719445c205b879eaebf13a420c7d7d75eac"
+ },
+ "source/delivery.json": {
+ "bytes": 2436,
+ "sha256": "013ec10f387fb5b8ba47ba08412151abce621df4b48b748a68a795c579cbe2aa"
+ },
+ "source/manifest.json": {
+ "bytes": 9960,
+ "sha256": "966612ba597a32eb61672119216ace1db57203a855b6e83388ecc41675d9f15e"
+ },
+ "source/nebula.json": {
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ "source/presentation.json": {
+ "bytes": 5972,
+ "sha256": "26c1a0c31be09b2f3ce8ee4e264b36f95f66554cbed2aeb58a2310b88f58ad24"
+ },
+ "source/provenance-references.json": {
+ "bytes": 1856,
+ "sha256": "9a749e5f63493aa373c47d09b1bfec5bd312c32b6896aafd0d51cb7a29e1d5d4"
+ },
+ "source/replay-references.json": {
+ "bytes": 1302,
+ "sha256": "d289333b8061b55f07f97bad5bef7a515123cc2e5bf845dec0a50724ba6df358"
+ },
+ "source/request.json": {
+ "bytes": 365,
+ "sha256": "043c6218aedd90269ec68822cfa2d729d731b7995150c0e01af8b5c68127e241"
+ }
+ },
+ "ownerPins": {
+ "tools/nebula/prepare.mts": "98a235b2977b45656af5280b9216361fe7a1def4a89c87f76ed09456bef0401e",
+ "tools/nebula/application/objects.ts": "3f2b3939e80769a7285b8677271939954ecbeb36f689d5e08731100c8807796c",
+ "labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts": "a9c3ebe7c2e88a8b3c624be2c0fef4f00f0b3d6b011809646486e1a6f7c3085a",
+ "labs/nebula/packages/volume-bake/src/compiler/bake.ts": "61419c3023157f4666cfd23a9edafbf749c84c79bfefdcd165506c55f224c719"
+ },
+ "initialColdState": {
+ "localCacheAbsent": true,
+ "omegaPreparedAbsent": true,
+ "stagingAbsent": true
+ },
+ "dependencies": "Fresh frozen offline pnpm install; cached package store, no node_modules links to parent checkout"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/context-regressions.json b/src/objects/omega-centauri/evidence/2026-09-20/context-regressions.json
new file mode 100644
index 0000000000..6c9f4d1612
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/context-regressions.json
@@ -0,0 +1,43 @@
+{
+ "baseRevision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "status": "Uncommitted shared working tree, implementation round 1; final independent review owned by pr380",
+ "sourcePins": [
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "bytes": 13264,
+ "sha256": "67286a0589f42d990d467d5c1e199661bc8b98b7102d152c527f34fc095e4a36"
+ },
+ {
+ "path": "site/test/prepared-context-navigation.test.mts",
+ "bytes": 23828,
+ "sha256": "bc80889dedb3c2b7906dd4792c7be68119442856bae95e4562bbf83c2c2713ba"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-universe-runtime.ts",
+ "bytes": 48720,
+ "sha256": "fd0d735b8f44cd639e3c9cf5333a4f4fbef1a2c9dbbbf8d288cf59a4441dae2f"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.ts",
+ "bytes": 873,
+ "sha256": "12a727775a8277fede7cd743642b960a30c10a7334f2e47186d97b6e8d5413f4"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.test.ts",
+ "bytes": 1302,
+ "sha256": "11a9abf0d929d27d84c698e3a1e9684778d65eafe7eb29b39a747e834b999beb"
+ },
+ {
+ "path": "src/renderers/css/sky/prepared-sky-runtime.test.ts",
+ "bytes": 52229,
+ "sha256": "3652d5086c4422b64ee66347d6909617693f443e1c7e577452796d5d4a5805e1"
+ }
+ ],
+ "tests": {
+ "runtimeAndPolicy": 16,
+ "navigationAndRouter": 71,
+ "mutations": 4
+ },
+ "scientificAssetsChanged": false,
+ "cameraAndLimitsChanged": false
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/context-review.json b/src/objects/omega-centauri/evidence/2026-09-20/context-review.json
new file mode 100644
index 0000000000..40ca6c2ffd
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/context-review.json
@@ -0,0 +1,29 @@
+[
+ {
+ "packet": "context-owner-r1",
+ "emptyTools": true,
+ "emptyMcp": true,
+ "complete": true,
+ "durationMs": 68972,
+ "changedInputsSinceReview": []
+ },
+ {
+ "packet": "context-transport-browser-r1",
+ "emptyTools": true,
+ "emptyMcp": true,
+ "complete": true,
+ "durationMs": 91279,
+ "changedInputsSinceReview": [
+ "tests/objects/browser/omega-centauri/native-focus-browser.mts"
+ ],
+ "supersededBy": "context-browser-restoration-r1"
+ },
+ {
+ "packet": "context-browser-restoration-r1",
+ "emptyTools": true,
+ "emptyMcp": true,
+ "complete": true,
+ "durationMs": 26803,
+ "changedInputsSinceReview": []
+ }
+]
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/dependent-r2-restore.json b/src/objects/omega-centauri/evidence/2026-09-20/dependent-r2-restore.json
new file mode 100644
index 0000000000..fd4d4f90c0
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/dependent-r2-restore.json
@@ -0,0 +1,2376 @@
+{
+ "status": "verified",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "claim": "Standard publication and independent cold byte verification; no source/payload edits or rebake",
+ "root": "/private/tmp/cssearth-pr390/output/pr390-audit/publication-closure/cold-final-context-1789940822961",
+ "startedAt": "2026-09-20T21:47:03.016Z",
+ "endedAt": "2026-09-20T21:47:04.549Z",
+ "durationSeconds": 1.5314474580000002,
+ "node": "v22.23.2",
+ "platform": "darwin",
+ "arch": "arm64",
+ "inputPins": [
+ {
+ "path": "src/objects/m31/object.json",
+ "sha256": "00adc87856c9653d79e8b1e581bfe0a5f431d3837ef08e496277af7dc2f4d1c5"
+ },
+ {
+ "path": "src/objects/m31/runtime-assets.json",
+ "sha256": "288f180fc548cdcba00f98afecf2db2121cc69a1c7fe9e2e5d736d6662432adb"
+ },
+ {
+ "path": "src/objects/m33/object.json",
+ "sha256": "20c22829b5f4a7127c747a5307043addc0d9584bca7671e022247857c0fab211"
+ },
+ {
+ "path": "src/objects/m33/runtime-assets.json",
+ "sha256": "ea94ccdd23f58680df91740f9e7a71c89446eac1533c559d08b434465ea545f0"
+ },
+ {
+ "path": "src/objects/nearby-universe/object.json",
+ "sha256": "61ea1293df390647595e3ac471ced4cdedcb17befba635cbc7b00e4be80bbbcf"
+ },
+ {
+ "path": "src/objects/nearby-universe/runtime-assets.json",
+ "sha256": "ba593972c6a03433669e88931779b3f82218394a4cf53531b733e9bee386f79c"
+ }
+ ],
+ "sourcePinsUnchanged": true,
+ "publisher": {
+ "selectedObjects": [
+ "m31",
+ "m33",
+ "nearby-universe"
+ ],
+ "assetsChecked": 200,
+ "alreadyPublished": 191,
+ "reportedMissesUploaded": 9,
+ "verified": 200,
+ "logSha256": "bce7bf4355d25a257f15223eb7ee65f50b838e97077e7be4067e2d3c7562ab7b",
+ "note": "Publisher checks the whole selected inventory closure. It reported 9 misses; the inventory has 4 changed entries, so at least 5 reported misses were unchanged entries. Individual upload-key identities are not retained by the publisher; independent GET verification proves all 4 changed keys live with exact bytes."
+ },
+ "changedKeys": [
+ {
+ "id": "m31",
+ "filename": "provenance.json",
+ "key": "runtime-assets/8fd017ce96e753da0d6fd53ed40040705e56c1a436605d6cd90bdecd98212404/provenance.json",
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8fd017ce96e753da0d6fd53ed40040705e56c1a436605d6cd90bdecd98212404/provenance.json",
+ "bytes": 38541,
+ "sha256": "8fd017ce96e753da0d6fd53ed40040705e56c1a436605d6cd90bdecd98212404",
+ "previous": {
+ "sha256": "119f50a4243d3be640ee70232bf9dee9bf056df5c14a444e5d7a21277c7bff2c",
+ "bytes": 38541
+ },
+ "httpStatus": 200
+ },
+ {
+ "id": "m33",
+ "filename": "provenance.json",
+ "key": "runtime-assets/24e8fb2b2e5a7d2ed514543b7f13f76b066a2eb859acf6ee54ab6df0ddb78981/provenance.json",
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/24e8fb2b2e5a7d2ed514543b7f13f76b066a2eb859acf6ee54ab6df0ddb78981/provenance.json",
+ "bytes": 37975,
+ "sha256": "24e8fb2b2e5a7d2ed514543b7f13f76b066a2eb859acf6ee54ab6df0ddb78981",
+ "previous": {
+ "sha256": "3af09ece5c85e824edd043a082fdeaf7c3f33bda27b564c4ddb133773ade74cd",
+ "bytes": 37975
+ },
+ "httpStatus": 200
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "points.json",
+ "key": "runtime-assets/595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee/points.json",
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee/points.json",
+ "bytes": 345406,
+ "sha256": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee",
+ "previous": {
+ "sha256": "c70262ba6e3ae288da6acd29971d1b8da0eeb33ae8a924fcfa39ff9b2eceb8d3",
+ "bytes": 345401
+ },
+ "httpStatus": 200
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "provenance.json",
+ "key": "runtime-assets/25fd338e082b947f3071dd635f47ba133067a9a091550e61521b7527e95a107f/provenance.json",
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/25fd338e082b947f3071dd635f47ba133067a9a091550e61521b7527e95a107f/provenance.json",
+ "bytes": 10363,
+ "sha256": "25fd338e082b947f3071dd635f47ba133067a9a091550e61521b7527e95a107f",
+ "previous": {
+ "sha256": "ee12abfd6a352def90493a13ade2c8297f6b75694240690980816e3482b582ea",
+ "bytes": 10363
+ },
+ "httpStatus": 200
+ }
+ ],
+ "unchangedAssetCount": 196,
+ "unchangedExamples": [
+ {
+ "id": "m31",
+ "filename": "presentation.json",
+ "sha256": "ef01151d319c4a1ad480d65a58cafaf24db1b3d28ac5deab5bcb0a420d36dbe1",
+ "bytes": 1857
+ },
+ {
+ "id": "m33",
+ "filename": "presentation.json",
+ "sha256": "9193e89bf84c577cf77f8fb4d584505b03a069264f009959ece9df73c6c59d71",
+ "bytes": 1799
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "cloud.webp",
+ "sha256": "ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c",
+ "bytes": 1706
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "presentation.json",
+ "sha256": "224e620caa689a95820bb15ad87bc37e7c7a6c2489db2136a441096246b10889",
+ "bytes": 490
+ }
+ ],
+ "restored": {
+ "installed": 200,
+ "reused": 0,
+ "skipped": 0
+ },
+ "repeat": {
+ "installed": 0,
+ "reused": 200,
+ "skipped": 0
+ },
+ "descriptors": [
+ {
+ "id": "m31",
+ "path": "prepared/image-layers.json",
+ "sha256": "16951a0b2f8fcccdd5c9b7c419cd6e486fda759448437ffe06fc6ba20a3b3957"
+ },
+ {
+ "id": "m33",
+ "path": "prepared/image-layers.json",
+ "sha256": "1e89f7d7ea9673eeebbbc3ad6da2695202dce3dfd2281830aa5fa5ff1c9e6572"
+ },
+ {
+ "id": "nearby-universe",
+ "path": "prepared/points.json",
+ "sha256": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee"
+ }
+ ],
+ "manifestBinding": [
+ {
+ "filename": "points.json",
+ "sha256": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee",
+ "bytes": 345406
+ },
+ {
+ "filename": "cloud.webp",
+ "sha256": "ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c",
+ "bytes": 1706
+ }
+ ],
+ "verified": [
+ {
+ "id": "m31",
+ "filename": "image-layers.json",
+ "bytes": 177609,
+ "sha256": "16951a0b2f8fcccdd5c9b7c419cd6e486fda759448437ffe06fc6ba20a3b3957",
+ "key": "runtime-assets/16951a0b2f8fcccdd5c9b7c419cd6e486fda759448437ffe06fc6ba20a3b3957/image-layers.json"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-00.webp",
+ "bytes": 1492,
+ "sha256": "6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353",
+ "key": "runtime-assets/6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353/layers/z-00.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-01.webp",
+ "bytes": 2038,
+ "sha256": "db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596",
+ "key": "runtime-assets/db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596/layers/z-01.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-02.webp",
+ "bytes": 2690,
+ "sha256": "7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05",
+ "key": "runtime-assets/7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05/layers/z-02.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-03.webp",
+ "bytes": 3726,
+ "sha256": "4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01",
+ "key": "runtime-assets/4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01/layers/z-03.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-04.webp",
+ "bytes": 4344,
+ "sha256": "7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4",
+ "key": "runtime-assets/7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4/layers/z-04.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-05.webp",
+ "bytes": 4898,
+ "sha256": "421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35",
+ "key": "runtime-assets/421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35/layers/z-05.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-06.webp",
+ "bytes": 5830,
+ "sha256": "567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137",
+ "key": "runtime-assets/567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137/layers/z-06.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-07.webp",
+ "bytes": 6744,
+ "sha256": "118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35",
+ "key": "runtime-assets/118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35/layers/z-07.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-08.webp",
+ "bytes": 7838,
+ "sha256": "77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4",
+ "key": "runtime-assets/77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4/layers/z-08.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-09.webp",
+ "bytes": 8660,
+ "sha256": "3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f",
+ "key": "runtime-assets/3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f/layers/z-09.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-10.webp",
+ "bytes": 9446,
+ "sha256": "9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93",
+ "key": "runtime-assets/9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93/layers/z-10.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-11.webp",
+ "bytes": 10042,
+ "sha256": "c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3",
+ "key": "runtime-assets/c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3/layers/z-11.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-12.webp",
+ "bytes": 10718,
+ "sha256": "b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572",
+ "key": "runtime-assets/b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572/layers/z-12.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-13.webp",
+ "bytes": 11342,
+ "sha256": "cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911",
+ "key": "runtime-assets/cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911/layers/z-13.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-14.webp",
+ "bytes": 11474,
+ "sha256": "d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433",
+ "key": "runtime-assets/d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433/layers/z-14.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-15.webp",
+ "bytes": 11362,
+ "sha256": "b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284",
+ "key": "runtime-assets/b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284/layers/z-15.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-16.webp",
+ "bytes": 11362,
+ "sha256": "b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284",
+ "key": "runtime-assets/b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284/layers/z-16.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-17.webp",
+ "bytes": 11474,
+ "sha256": "d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433",
+ "key": "runtime-assets/d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433/layers/z-17.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-18.webp",
+ "bytes": 11342,
+ "sha256": "cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911",
+ "key": "runtime-assets/cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911/layers/z-18.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-19.webp",
+ "bytes": 10718,
+ "sha256": "b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572",
+ "key": "runtime-assets/b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572/layers/z-19.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-20.webp",
+ "bytes": 10042,
+ "sha256": "c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3",
+ "key": "runtime-assets/c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3/layers/z-20.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-21.webp",
+ "bytes": 9446,
+ "sha256": "9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93",
+ "key": "runtime-assets/9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93/layers/z-21.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-22.webp",
+ "bytes": 8660,
+ "sha256": "3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f",
+ "key": "runtime-assets/3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f/layers/z-22.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-23.webp",
+ "bytes": 7838,
+ "sha256": "77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4",
+ "key": "runtime-assets/77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4/layers/z-23.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-24.webp",
+ "bytes": 6744,
+ "sha256": "118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35",
+ "key": "runtime-assets/118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35/layers/z-24.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-25.webp",
+ "bytes": 5830,
+ "sha256": "567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137",
+ "key": "runtime-assets/567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137/layers/z-25.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-26.webp",
+ "bytes": 4898,
+ "sha256": "421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35",
+ "key": "runtime-assets/421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35/layers/z-26.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-27.webp",
+ "bytes": 4344,
+ "sha256": "7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4",
+ "key": "runtime-assets/7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4/layers/z-27.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-28.webp",
+ "bytes": 3726,
+ "sha256": "4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01",
+ "key": "runtime-assets/4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01/layers/z-28.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-29.webp",
+ "bytes": 2690,
+ "sha256": "7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05",
+ "key": "runtime-assets/7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05/layers/z-29.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-30.webp",
+ "bytes": 2038,
+ "sha256": "db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596",
+ "key": "runtime-assets/db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596/layers/z-30.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-31.webp",
+ "bytes": 1492,
+ "sha256": "6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353",
+ "key": "runtime-assets/6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353/layers/z-31.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-detail.webp",
+ "bytes": 3247660,
+ "sha256": "7da54ef71c1872bf2da0180a62f5d516a8f524b56da9405c3bf8c5a1c6b0009c",
+ "key": "runtime-assets/7da54ef71c1872bf2da0180a62f5d516a8f524b56da9405c3bf8c5a1c6b0009c/layers/z-detail.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-01.webp",
+ "bytes": 548,
+ "sha256": "e78eeeb4e4bff12c71779eb59ad753e333a202b46a8e6482932b8379509f0f5c",
+ "key": "runtime-assets/e78eeeb4e4bff12c71779eb59ad753e333a202b46a8e6482932b8379509f0f5c/layers/x-01.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-02.webp",
+ "bytes": 966,
+ "sha256": "d464be75e453dc4a95e26c7fb38c5f46e6a7a0eb37497dad32c3cfe0db95938f",
+ "key": "runtime-assets/d464be75e453dc4a95e26c7fb38c5f46e6a7a0eb37497dad32c3cfe0db95938f/layers/x-02.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-03.webp",
+ "bytes": 1312,
+ "sha256": "a973db4172b7d63477c0ca311ddd238f7f13fa3bbc6a1bdb8f4b1e0b9b11896a",
+ "key": "runtime-assets/a973db4172b7d63477c0ca311ddd238f7f13fa3bbc6a1bdb8f4b1e0b9b11896a/layers/x-03.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-04.webp",
+ "bytes": 1678,
+ "sha256": "ab3c45ef714a5e9e051f58cd1089d4c5d0d83c93d0ef5db193caab6e1a445d6e",
+ "key": "runtime-assets/ab3c45ef714a5e9e051f58cd1089d4c5d0d83c93d0ef5db193caab6e1a445d6e/layers/x-04.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-05.webp",
+ "bytes": 1600,
+ "sha256": "d46f27c04493425a415a5d39e2a9e9acc2eb2f9b04682e3db83fad6dfe638e49",
+ "key": "runtime-assets/d46f27c04493425a415a5d39e2a9e9acc2eb2f9b04682e3db83fad6dfe638e49/layers/x-05.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-06.webp",
+ "bytes": 1634,
+ "sha256": "01f47abf55059be7fd2647523fc87600e2d63834f8c7e70640b5711c15638c9b",
+ "key": "runtime-assets/01f47abf55059be7fd2647523fc87600e2d63834f8c7e70640b5711c15638c9b/layers/x-06.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-07.webp",
+ "bytes": 1768,
+ "sha256": "2de4319c85aaab323cedaeedfc86bdab53db0bfc33019e6eeb3d8ccca2ae97f1",
+ "key": "runtime-assets/2de4319c85aaab323cedaeedfc86bdab53db0bfc33019e6eeb3d8ccca2ae97f1/layers/x-07.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-08.webp",
+ "bytes": 1836,
+ "sha256": "e71c5a2506fdceedb650dd2babc5e73cc69fe4beea48278782749fd1840e5a6d",
+ "key": "runtime-assets/e71c5a2506fdceedb650dd2babc5e73cc69fe4beea48278782749fd1840e5a6d/layers/x-08.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-09.webp",
+ "bytes": 1984,
+ "sha256": "6da86dca8873f6d5bad23a049050ad14b0bb186484dba9d4e5f8601f95d9dcf6",
+ "key": "runtime-assets/6da86dca8873f6d5bad23a049050ad14b0bb186484dba9d4e5f8601f95d9dcf6/layers/x-09.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-10.webp",
+ "bytes": 2026,
+ "sha256": "eb854b16b3824e8e47f87f879e56808868f689e036cd4483dd01bd78457f83a8",
+ "key": "runtime-assets/eb854b16b3824e8e47f87f879e56808868f689e036cd4483dd01bd78457f83a8/layers/x-10.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-11.webp",
+ "bytes": 2448,
+ "sha256": "066d7e6d9b2057384cca5e78af641df790f5ccabdee90fe68b0ac793e3714267",
+ "key": "runtime-assets/066d7e6d9b2057384cca5e78af641df790f5ccabdee90fe68b0ac793e3714267/layers/x-11.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-12.webp",
+ "bytes": 2404,
+ "sha256": "f094f5781ece11c3306c3343bcf0267b3a77ba83f69aa4045ac222bcca5863c5",
+ "key": "runtime-assets/f094f5781ece11c3306c3343bcf0267b3a77ba83f69aa4045ac222bcca5863c5/layers/x-12.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-13.webp",
+ "bytes": 1948,
+ "sha256": "e55df8e13829273a44e7c03d2d210d3ee8ac8a444f3e4b90a52d497427260412",
+ "key": "runtime-assets/e55df8e13829273a44e7c03d2d210d3ee8ac8a444f3e4b90a52d497427260412/layers/x-13.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-14.webp",
+ "bytes": 2264,
+ "sha256": "add47432f770ba703f598efb70a50dac12af9d03ead2de75d3c1e9c878f91d30",
+ "key": "runtime-assets/add47432f770ba703f598efb70a50dac12af9d03ead2de75d3c1e9c878f91d30/layers/x-14.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-15.webp",
+ "bytes": 2338,
+ "sha256": "900b1aada73e64f398e8a0245f28ae7bf651740879f64c71f18ac604293e407d",
+ "key": "runtime-assets/900b1aada73e64f398e8a0245f28ae7bf651740879f64c71f18ac604293e407d/layers/x-15.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-16.webp",
+ "bytes": 1958,
+ "sha256": "3fae9a41364c44e2687c51fd3fd215a5af3ab59f46dce8b0a7fa1d8e80f1e76f",
+ "key": "runtime-assets/3fae9a41364c44e2687c51fd3fd215a5af3ab59f46dce8b0a7fa1d8e80f1e76f/layers/x-16.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-17.webp",
+ "bytes": 1864,
+ "sha256": "262b930cf8a41787c65a3b2f4089106f5a9d379e122f56067d4239a84d65e61e",
+ "key": "runtime-assets/262b930cf8a41787c65a3b2f4089106f5a9d379e122f56067d4239a84d65e61e/layers/x-17.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-18.webp",
+ "bytes": 2090,
+ "sha256": "886d478112b4f95449d46d239d253649c8c54ac652349ca8ddbed7436ac80a61",
+ "key": "runtime-assets/886d478112b4f95449d46d239d253649c8c54ac652349ca8ddbed7436ac80a61/layers/x-18.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-19.webp",
+ "bytes": 1684,
+ "sha256": "15390b6c4c5c8cfd19c85857df4b9aaf80f0dd701c62ef665ad75c445cfd00ea",
+ "key": "runtime-assets/15390b6c4c5c8cfd19c85857df4b9aaf80f0dd701c62ef665ad75c445cfd00ea/layers/x-19.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-20.webp",
+ "bytes": 1830,
+ "sha256": "d4741e12d20490cc650b95df8c8b7e1ff2615f148be0a759592bd7684b566729",
+ "key": "runtime-assets/d4741e12d20490cc650b95df8c8b7e1ff2615f148be0a759592bd7684b566729/layers/x-20.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-21.webp",
+ "bytes": 1722,
+ "sha256": "14fd79cc7ad12c1d9f7f9e730c0a5d928e8b4eede72216c4074a15b778ef45df",
+ "key": "runtime-assets/14fd79cc7ad12c1d9f7f9e730c0a5d928e8b4eede72216c4074a15b778ef45df/layers/x-21.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-22.webp",
+ "bytes": 1582,
+ "sha256": "8806454f09e05a8242169fb729ffd62353ad6dec22e6372e20936ed6cd9afcb8",
+ "key": "runtime-assets/8806454f09e05a8242169fb729ffd62353ad6dec22e6372e20936ed6cd9afcb8/layers/x-22.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-23.webp",
+ "bytes": 1458,
+ "sha256": "7229f90e841349a0e3040a7ba36cb8e6a6fd031786ba276d652e9966cca9b352",
+ "key": "runtime-assets/7229f90e841349a0e3040a7ba36cb8e6a6fd031786ba276d652e9966cca9b352/layers/x-23.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-24.webp",
+ "bytes": 1348,
+ "sha256": "012fa982feec9ee649988eefe9d359394c6266c67397c086afbc7bfecde4a3ea",
+ "key": "runtime-assets/012fa982feec9ee649988eefe9d359394c6266c67397c086afbc7bfecde4a3ea/layers/x-24.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-25.webp",
+ "bytes": 942,
+ "sha256": "5275337df162438547ce51109b8825bc30c08a4b58e4e982945f343643d91058",
+ "key": "runtime-assets/5275337df162438547ce51109b8825bc30c08a4b58e4e982945f343643d91058/layers/x-25.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-26.webp",
+ "bytes": 662,
+ "sha256": "aaf60d06e5b0a7b9136f9aac1a518c1bf59c72b2d7734f5f4b74305a3c3bc3ea",
+ "key": "runtime-assets/aaf60d06e5b0a7b9136f9aac1a518c1bf59c72b2d7734f5f4b74305a3c3bc3ea/layers/x-26.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-27.webp",
+ "bytes": 398,
+ "sha256": "1cb5d9e6b724c19e5800d8fe1a093cce2ed89146dff460952b703374491e43ee",
+ "key": "runtime-assets/1cb5d9e6b724c19e5800d8fe1a093cce2ed89146dff460952b703374491e43ee/layers/x-27.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-28.webp",
+ "bytes": 112,
+ "sha256": "7be4374bf82ad267a3412e7c318c13e86c03b6160fb7d3ad1ef1f10800af2bde",
+ "key": "runtime-assets/7be4374bf82ad267a3412e7c318c13e86c03b6160fb7d3ad1ef1f10800af2bde/layers/x-28.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-00.webp",
+ "bytes": 118,
+ "sha256": "c0ecd45f8bce89e8bcb5b6b2b419e8d4bb2ce63967154a7a3cfe9a854e8a30c8",
+ "key": "runtime-assets/c0ecd45f8bce89e8bcb5b6b2b419e8d4bb2ce63967154a7a3cfe9a854e8a30c8/layers/y-00.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-01.webp",
+ "bytes": 516,
+ "sha256": "d369ea41b48f54cde8701fa2ca046a1500658967a22e472af9671d5f07a611a9",
+ "key": "runtime-assets/d369ea41b48f54cde8701fa2ca046a1500658967a22e472af9671d5f07a611a9/layers/y-01.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-02.webp",
+ "bytes": 692,
+ "sha256": "670683e45cc76f09d2fabaa8c33d0de4f985e6a3e408dafbc9f42853c450ebae",
+ "key": "runtime-assets/670683e45cc76f09d2fabaa8c33d0de4f985e6a3e408dafbc9f42853c450ebae/layers/y-02.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-03.webp",
+ "bytes": 942,
+ "sha256": "ef21f88b9a5a3fe6caf0f5eb77dec87fa0e9fa776ef208ab3177f75b83d779ef",
+ "key": "runtime-assets/ef21f88b9a5a3fe6caf0f5eb77dec87fa0e9fa776ef208ab3177f75b83d779ef/layers/y-03.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-04.webp",
+ "bytes": 1392,
+ "sha256": "1877add88dec4803e86ad04981d6a616125cbb4a9b23828e10661ac0df3a29a4",
+ "key": "runtime-assets/1877add88dec4803e86ad04981d6a616125cbb4a9b23828e10661ac0df3a29a4/layers/y-04.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-05.webp",
+ "bytes": 1452,
+ "sha256": "ad8c4648010f0facc726cf6f64de2421045c05b50756876960bf550f6168e02e",
+ "key": "runtime-assets/ad8c4648010f0facc726cf6f64de2421045c05b50756876960bf550f6168e02e/layers/y-05.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-06.webp",
+ "bytes": 1398,
+ "sha256": "872d95de54845c3768c0e638de78f8f43c6fc1f9bc233988bea64d53ca86d821",
+ "key": "runtime-assets/872d95de54845c3768c0e638de78f8f43c6fc1f9bc233988bea64d53ca86d821/layers/y-06.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-07.webp",
+ "bytes": 1460,
+ "sha256": "b3129189d8e02ba191f73fdd6ef0f74a6fad646cabbc7df354205e8438b90b57",
+ "key": "runtime-assets/b3129189d8e02ba191f73fdd6ef0f74a6fad646cabbc7df354205e8438b90b57/layers/y-07.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-08.webp",
+ "bytes": 1540,
+ "sha256": "ec870ddaeaa7615f5d8ab0f5d46a78d320aaf9fc59b806e2ffa1df85a91057ec",
+ "key": "runtime-assets/ec870ddaeaa7615f5d8ab0f5d46a78d320aaf9fc59b806e2ffa1df85a91057ec/layers/y-08.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-09.webp",
+ "bytes": 1626,
+ "sha256": "c288ad201a5644b8521a5d9a20a67479b89d2bcd24354ab17b9f4ef67d845db3",
+ "key": "runtime-assets/c288ad201a5644b8521a5d9a20a67479b89d2bcd24354ab17b9f4ef67d845db3/layers/y-09.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-10.webp",
+ "bytes": 1628,
+ "sha256": "1d9665c4314ff93e641de2368f9ea887d2fe6f81c570a7f3a3606d4a952084e4",
+ "key": "runtime-assets/1d9665c4314ff93e641de2368f9ea887d2fe6f81c570a7f3a3606d4a952084e4/layers/y-10.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-11.webp",
+ "bytes": 1474,
+ "sha256": "cfb523ee09f4f5a755910c5b86f3b8227ef46f6035eb2d5493eedcc84583c098",
+ "key": "runtime-assets/cfb523ee09f4f5a755910c5b86f3b8227ef46f6035eb2d5493eedcc84583c098/layers/y-11.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-12.webp",
+ "bytes": 1758,
+ "sha256": "dfafcb4e68bac5394285b3d7ea589ff43444f3732d76db17d363e59fa694d454",
+ "key": "runtime-assets/dfafcb4e68bac5394285b3d7ea589ff43444f3732d76db17d363e59fa694d454/layers/y-12.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-13.webp",
+ "bytes": 1736,
+ "sha256": "455eb90107ff596122362fb42d9f979f55a65734e9290eefd3a2888155999ce4",
+ "key": "runtime-assets/455eb90107ff596122362fb42d9f979f55a65734e9290eefd3a2888155999ce4/layers/y-13.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-14.webp",
+ "bytes": 2118,
+ "sha256": "334b0a6ce53fcdeff82133aeabba8286dbe71e1463915b9c8af55663b60170dc",
+ "key": "runtime-assets/334b0a6ce53fcdeff82133aeabba8286dbe71e1463915b9c8af55663b60170dc/layers/y-14.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-15.webp",
+ "bytes": 2204,
+ "sha256": "3f368bbbe73ca3a4a9fdcf6aecd1413aee471aaf8eba6da51df7e4c7d997fe1f",
+ "key": "runtime-assets/3f368bbbe73ca3a4a9fdcf6aecd1413aee471aaf8eba6da51df7e4c7d997fe1f/layers/y-15.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-16.webp",
+ "bytes": 1836,
+ "sha256": "c895666cb91769fca707167be46d0ba6f3285e295aa0cf06afcb734bf775b3be",
+ "key": "runtime-assets/c895666cb91769fca707167be46d0ba6f3285e295aa0cf06afcb734bf775b3be/layers/y-16.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-17.webp",
+ "bytes": 2068,
+ "sha256": "c3d435478a2f011e0f09d0a99293cfcdb88b57ffbb1613635999478bf4b50bbf",
+ "key": "runtime-assets/c3d435478a2f011e0f09d0a99293cfcdb88b57ffbb1613635999478bf4b50bbf/layers/y-17.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-18.webp",
+ "bytes": 1768,
+ "sha256": "69e82473c09d985ee2b9d163c9ff249f0bca837b0b47834575435e2b05edcf6f",
+ "key": "runtime-assets/69e82473c09d985ee2b9d163c9ff249f0bca837b0b47834575435e2b05edcf6f/layers/y-18.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-19.webp",
+ "bytes": 1720,
+ "sha256": "0ca061c1e4cb504647e15696f74fde84b5f3a9685f62562a0342536c2ca724b4",
+ "key": "runtime-assets/0ca061c1e4cb504647e15696f74fde84b5f3a9685f62562a0342536c2ca724b4/layers/y-19.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-20.webp",
+ "bytes": 1766,
+ "sha256": "040001269ace310d7afc32e4dffb240878009d3d033b64b399c0c49c720bb2f6",
+ "key": "runtime-assets/040001269ace310d7afc32e4dffb240878009d3d033b64b399c0c49c720bb2f6/layers/y-20.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-21.webp",
+ "bytes": 1548,
+ "sha256": "7f427707bf01b3682239f90b165a3811617fd97dfab59b4f18903402a643b615",
+ "key": "runtime-assets/7f427707bf01b3682239f90b165a3811617fd97dfab59b4f18903402a643b615/layers/y-21.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-22.webp",
+ "bytes": 1750,
+ "sha256": "ced11e3d3bd1f5ef96646ce2f7b7f7e8f54047e57398fda77c8d24146659b429",
+ "key": "runtime-assets/ced11e3d3bd1f5ef96646ce2f7b7f7e8f54047e57398fda77c8d24146659b429/layers/y-22.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-23.webp",
+ "bytes": 1636,
+ "sha256": "1ab16b4007a25fcc92b58788f7be38c3e928e5263bf940f5d3e183cab2d88a57",
+ "key": "runtime-assets/1ab16b4007a25fcc92b58788f7be38c3e928e5263bf940f5d3e183cab2d88a57/layers/y-23.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-24.webp",
+ "bytes": 1586,
+ "sha256": "262b3347921ae8a48a60a2395616aeeeca477312bdc8e4343d9531b392b8c711",
+ "key": "runtime-assets/262b3347921ae8a48a60a2395616aeeeca477312bdc8e4343d9531b392b8c711/layers/y-24.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-25.webp",
+ "bytes": 1684,
+ "sha256": "e198fb0a565b3402bf3789a166d618cd645f736600158378d2ca73e112300976",
+ "key": "runtime-assets/e198fb0a565b3402bf3789a166d618cd645f736600158378d2ca73e112300976/layers/y-25.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-26.webp",
+ "bytes": 1392,
+ "sha256": "bdb46824a992a8cd31c9c91cd6bc0987d390359762f70eb12c194d17288985f2",
+ "key": "runtime-assets/bdb46824a992a8cd31c9c91cd6bc0987d390359762f70eb12c194d17288985f2/layers/y-26.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-27.webp",
+ "bytes": 1220,
+ "sha256": "0764e92999e6a776fd15124558847e763a0bf585e3fe33fa36fe90ddaf7c27ed",
+ "key": "runtime-assets/0764e92999e6a776fd15124558847e763a0bf585e3fe33fa36fe90ddaf7c27ed/layers/y-27.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-28.webp",
+ "bytes": 1072,
+ "sha256": "ad902a30624a2659ab0e680739684698a3e1348305e6ee0920f879dd9a04fdb7",
+ "key": "runtime-assets/ad902a30624a2659ab0e680739684698a3e1348305e6ee0920f879dd9a04fdb7/layers/y-28.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-29.webp",
+ "bytes": 856,
+ "sha256": "60da9e0b67cf85fbec7fbab7530827fdc874a0443e225acfd9d21ba7e98357ab",
+ "key": "runtime-assets/60da9e0b67cf85fbec7fbab7530827fdc874a0443e225acfd9d21ba7e98357ab/layers/y-29.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-30.webp",
+ "bytes": 726,
+ "sha256": "61303a62465dd706e6f1ddb23dba2c16c89d0493e67c16b67f38e890242cb4f3",
+ "key": "runtime-assets/61303a62465dd706e6f1ddb23dba2c16c89d0493e67c16b67f38e890242cb4f3/layers/y-30.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "provenance.json",
+ "bytes": 38541,
+ "sha256": "8fd017ce96e753da0d6fd53ed40040705e56c1a436605d6cd90bdecd98212404",
+ "key": "runtime-assets/8fd017ce96e753da0d6fd53ed40040705e56c1a436605d6cd90bdecd98212404/provenance.json"
+ },
+ {
+ "id": "m31",
+ "filename": "presentation.json",
+ "bytes": 1857,
+ "sha256": "ef01151d319c4a1ad480d65a58cafaf24db1b3d28ac5deab5bcb0a420d36dbe1",
+ "key": "runtime-assets/ef01151d319c4a1ad480d65a58cafaf24db1b3d28ac5deab5bcb0a420d36dbe1/presentation.json"
+ },
+ {
+ "id": "m31",
+ "filename": "datasets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9.webp",
+ "bytes": 85990,
+ "sha256": "4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9",
+ "key": "runtime-assets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9/datasets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "image-layers.json",
+ "bytes": 183493,
+ "sha256": "1e89f7d7ea9673eeebbbc3ad6da2695202dce3dfd2281830aa5fa5ff1c9e6572",
+ "key": "runtime-assets/1e89f7d7ea9673eeebbbc3ad6da2695202dce3dfd2281830aa5fa5ff1c9e6572/image-layers.json"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-00.webp",
+ "bytes": 4654,
+ "sha256": "d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe",
+ "key": "runtime-assets/d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe/layers/z-00.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-01.webp",
+ "bytes": 6346,
+ "sha256": "5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd",
+ "key": "runtime-assets/5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd/layers/z-01.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-02.webp",
+ "bytes": 7994,
+ "sha256": "cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f",
+ "key": "runtime-assets/cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f/layers/z-02.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-03.webp",
+ "bytes": 9694,
+ "sha256": "e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8",
+ "key": "runtime-assets/e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8/layers/z-03.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-04.webp",
+ "bytes": 11696,
+ "sha256": "4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb",
+ "key": "runtime-assets/4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb/layers/z-04.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-05.webp",
+ "bytes": 12886,
+ "sha256": "90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c",
+ "key": "runtime-assets/90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c/layers/z-05.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-06.webp",
+ "bytes": 15030,
+ "sha256": "d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4",
+ "key": "runtime-assets/d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4/layers/z-06.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-07.webp",
+ "bytes": 17242,
+ "sha256": "476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d",
+ "key": "runtime-assets/476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d/layers/z-07.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-08.webp",
+ "bytes": 19274,
+ "sha256": "4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67",
+ "key": "runtime-assets/4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67/layers/z-08.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-09.webp",
+ "bytes": 21476,
+ "sha256": "ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e",
+ "key": "runtime-assets/ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e/layers/z-09.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-10.webp",
+ "bytes": 22888,
+ "sha256": "6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b",
+ "key": "runtime-assets/6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b/layers/z-10.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-11.webp",
+ "bytes": 24308,
+ "sha256": "69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee",
+ "key": "runtime-assets/69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee/layers/z-11.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-12.webp",
+ "bytes": 26204,
+ "sha256": "cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b",
+ "key": "runtime-assets/cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b/layers/z-12.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-13.webp",
+ "bytes": 27288,
+ "sha256": "7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac",
+ "key": "runtime-assets/7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac/layers/z-13.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-14.webp",
+ "bytes": 27622,
+ "sha256": "4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8",
+ "key": "runtime-assets/4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8/layers/z-14.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-15.webp",
+ "bytes": 27546,
+ "sha256": "c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3",
+ "key": "runtime-assets/c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3/layers/z-15.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-16.webp",
+ "bytes": 27546,
+ "sha256": "c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3",
+ "key": "runtime-assets/c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3/layers/z-16.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-17.webp",
+ "bytes": 27622,
+ "sha256": "4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8",
+ "key": "runtime-assets/4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8/layers/z-17.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-18.webp",
+ "bytes": 27288,
+ "sha256": "7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac",
+ "key": "runtime-assets/7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac/layers/z-18.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-19.webp",
+ "bytes": 26204,
+ "sha256": "cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b",
+ "key": "runtime-assets/cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b/layers/z-19.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-20.webp",
+ "bytes": 24308,
+ "sha256": "69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee",
+ "key": "runtime-assets/69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee/layers/z-20.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-21.webp",
+ "bytes": 22888,
+ "sha256": "6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b",
+ "key": "runtime-assets/6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b/layers/z-21.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-22.webp",
+ "bytes": 21476,
+ "sha256": "ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e",
+ "key": "runtime-assets/ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e/layers/z-22.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-23.webp",
+ "bytes": 19274,
+ "sha256": "4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67",
+ "key": "runtime-assets/4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67/layers/z-23.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-24.webp",
+ "bytes": 17242,
+ "sha256": "476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d",
+ "key": "runtime-assets/476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d/layers/z-24.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-25.webp",
+ "bytes": 15030,
+ "sha256": "d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4",
+ "key": "runtime-assets/d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4/layers/z-25.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-26.webp",
+ "bytes": 12886,
+ "sha256": "90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c",
+ "key": "runtime-assets/90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c/layers/z-26.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-27.webp",
+ "bytes": 11696,
+ "sha256": "4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb",
+ "key": "runtime-assets/4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb/layers/z-27.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-28.webp",
+ "bytes": 9694,
+ "sha256": "e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8",
+ "key": "runtime-assets/e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8/layers/z-28.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-29.webp",
+ "bytes": 7994,
+ "sha256": "cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f",
+ "key": "runtime-assets/cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f/layers/z-29.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-30.webp",
+ "bytes": 6346,
+ "sha256": "5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd",
+ "key": "runtime-assets/5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd/layers/z-30.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-31.webp",
+ "bytes": 4654,
+ "sha256": "d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe",
+ "key": "runtime-assets/d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe/layers/z-31.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-detail.webp",
+ "bytes": 3404342,
+ "sha256": "683c13ef47fe913904b73282ecc98e5a2577e3a4a616d4ac1f4f737e5b3e8c31",
+ "key": "runtime-assets/683c13ef47fe913904b73282ecc98e5a2577e3a4a616d4ac1f4f737e5b3e8c31/layers/z-detail.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-00.webp",
+ "bytes": 122,
+ "sha256": "35d5dc3ad1a75292bb9da6897e37d85c72a04c7c7c929dd157999a1c3f62731a",
+ "key": "runtime-assets/35d5dc3ad1a75292bb9da6897e37d85c72a04c7c7c929dd157999a1c3f62731a/layers/x-00.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-01.webp",
+ "bytes": 1722,
+ "sha256": "72c8864fee520d58bc55486580be019ad22235f9613d4b650aa714ce8455d5c9",
+ "key": "runtime-assets/72c8864fee520d58bc55486580be019ad22235f9613d4b650aa714ce8455d5c9/layers/x-01.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-02.webp",
+ "bytes": 2288,
+ "sha256": "8116cd8f718623d7a5c64780852f43743d77e6cb502015188015734b62a77120",
+ "key": "runtime-assets/8116cd8f718623d7a5c64780852f43743d77e6cb502015188015734b62a77120/layers/x-02.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-03.webp",
+ "bytes": 2254,
+ "sha256": "7a039f59b82b66140d7846e6c42af9610e5d1528d0a5753764038d076c6070ef",
+ "key": "runtime-assets/7a039f59b82b66140d7846e6c42af9610e5d1528d0a5753764038d076c6070ef/layers/x-03.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-04.webp",
+ "bytes": 2494,
+ "sha256": "140005f9292e15140049210e50907fdac8f25c1d23ae5cf53420ba0076312931",
+ "key": "runtime-assets/140005f9292e15140049210e50907fdac8f25c1d23ae5cf53420ba0076312931/layers/x-04.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-05.webp",
+ "bytes": 2638,
+ "sha256": "56868d53d8504d62f6d128d97ed8e64f28bf47014b6e61d430d241ba37ca0766",
+ "key": "runtime-assets/56868d53d8504d62f6d128d97ed8e64f28bf47014b6e61d430d241ba37ca0766/layers/x-05.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-06.webp",
+ "bytes": 2756,
+ "sha256": "9da3c0bf6600982ba8311694c4a3720f050f172d244e917302b07ec70226e146",
+ "key": "runtime-assets/9da3c0bf6600982ba8311694c4a3720f050f172d244e917302b07ec70226e146/layers/x-06.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-07.webp",
+ "bytes": 2722,
+ "sha256": "f20ac9fd2dd466910602c93e5e929c30735717bf8b292ee05e818374a19120d7",
+ "key": "runtime-assets/f20ac9fd2dd466910602c93e5e929c30735717bf8b292ee05e818374a19120d7/layers/x-07.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-08.webp",
+ "bytes": 2922,
+ "sha256": "3b081a11d1da314987c4c68de6fee632676d88751610f77bafb6b4a66130ce40",
+ "key": "runtime-assets/3b081a11d1da314987c4c68de6fee632676d88751610f77bafb6b4a66130ce40/layers/x-08.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-09.webp",
+ "bytes": 2894,
+ "sha256": "b67d875f5733c94041882ff384aa090a2aaf6ace1a6bb739d5732fe81862fa86",
+ "key": "runtime-assets/b67d875f5733c94041882ff384aa090a2aaf6ace1a6bb739d5732fe81862fa86/layers/x-09.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-10.webp",
+ "bytes": 2932,
+ "sha256": "a4aa9ab2e15cd8ff874bcecbcaa1ada6503981efbe3808d9aeefbd0a1bdc4f10",
+ "key": "runtime-assets/a4aa9ab2e15cd8ff874bcecbcaa1ada6503981efbe3808d9aeefbd0a1bdc4f10/layers/x-10.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-11.webp",
+ "bytes": 2880,
+ "sha256": "a57aa43aa5f8edf7f45ace919e65bfb31c8ecffe49d7e09458f6f8ea271a4ac2",
+ "key": "runtime-assets/a57aa43aa5f8edf7f45ace919e65bfb31c8ecffe49d7e09458f6f8ea271a4ac2/layers/x-11.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-12.webp",
+ "bytes": 3924,
+ "sha256": "f77aab646f679802c1d539bf9698cbb70a9f48e36358a0e8ca7fb0f081ff2b0f",
+ "key": "runtime-assets/f77aab646f679802c1d539bf9698cbb70a9f48e36358a0e8ca7fb0f081ff2b0f/layers/x-12.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-13.webp",
+ "bytes": 3528,
+ "sha256": "faf9f6f168641a7afea3de68bdc6d0083f7a3f82941aaaa2900e6b194ce598e5",
+ "key": "runtime-assets/faf9f6f168641a7afea3de68bdc6d0083f7a3f82941aaaa2900e6b194ce598e5/layers/x-13.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-14.webp",
+ "bytes": 3800,
+ "sha256": "5d69273f953fa930eebed5d89e71bf2194ee4557ce8cce50a0f0aff166b282cf",
+ "key": "runtime-assets/5d69273f953fa930eebed5d89e71bf2194ee4557ce8cce50a0f0aff166b282cf/layers/x-14.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-15.webp",
+ "bytes": 4056,
+ "sha256": "e39d0c714ffbd3e62c237a5abeee06fd33cacaa2563370f95cd6cc8302e82e43",
+ "key": "runtime-assets/e39d0c714ffbd3e62c237a5abeee06fd33cacaa2563370f95cd6cc8302e82e43/layers/x-15.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-16.webp",
+ "bytes": 4036,
+ "sha256": "d92a95e7a7f0feacdb100ca9b921f0ae88e63826e499df33c361291aa493a986",
+ "key": "runtime-assets/d92a95e7a7f0feacdb100ca9b921f0ae88e63826e499df33c361291aa493a986/layers/x-16.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-17.webp",
+ "bytes": 3730,
+ "sha256": "0ec312513b4cbae6c094f930f2dfe5fc42237ea88a37b2f34dd16e0ea37a0a54",
+ "key": "runtime-assets/0ec312513b4cbae6c094f930f2dfe5fc42237ea88a37b2f34dd16e0ea37a0a54/layers/x-17.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-18.webp",
+ "bytes": 4060,
+ "sha256": "2fd1ecf0fe47d904eda3b46f8249771544b4cf60c25b851352be383b6adc486d",
+ "key": "runtime-assets/2fd1ecf0fe47d904eda3b46f8249771544b4cf60c25b851352be383b6adc486d/layers/x-18.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-19.webp",
+ "bytes": 3932,
+ "sha256": "531f166a3156312adfd5ab1ce56b1853e405af05f0f9d80a75d9a9939177276b",
+ "key": "runtime-assets/531f166a3156312adfd5ab1ce56b1853e405af05f0f9d80a75d9a9939177276b/layers/x-19.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-20.webp",
+ "bytes": 3202,
+ "sha256": "901a31717542c9214518cff2a3901ec8e7bb1e0a423df020704691738eecc92c",
+ "key": "runtime-assets/901a31717542c9214518cff2a3901ec8e7bb1e0a423df020704691738eecc92c/layers/x-20.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-21.webp",
+ "bytes": 2944,
+ "sha256": "b694f6808551b3ed520c6979bb19b3d6ec36358c16011e13153ba09721d5ee1a",
+ "key": "runtime-assets/b694f6808551b3ed520c6979bb19b3d6ec36358c16011e13153ba09721d5ee1a/layers/x-21.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-22.webp",
+ "bytes": 3066,
+ "sha256": "ef8a1f0cc3267af10d6c294b6c562da9738fa055a0f8d726505644715112d110",
+ "key": "runtime-assets/ef8a1f0cc3267af10d6c294b6c562da9738fa055a0f8d726505644715112d110/layers/x-22.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-23.webp",
+ "bytes": 3060,
+ "sha256": "9e81aabfc770c9d936458e061c6c88bb6a12a0de28ffd7752432b0b87aa7b8a5",
+ "key": "runtime-assets/9e81aabfc770c9d936458e061c6c88bb6a12a0de28ffd7752432b0b87aa7b8a5/layers/x-23.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-24.webp",
+ "bytes": 3190,
+ "sha256": "6075a88ffaa26776ba0ffa8f1bd7247e4902548a2684ad85dacd18c6590d893c",
+ "key": "runtime-assets/6075a88ffaa26776ba0ffa8f1bd7247e4902548a2684ad85dacd18c6590d893c/layers/x-24.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-25.webp",
+ "bytes": 2910,
+ "sha256": "d1421dc8b2d2d166c6fbda6dab3c6dd88be88df48e61a792875a6b024490d73f",
+ "key": "runtime-assets/d1421dc8b2d2d166c6fbda6dab3c6dd88be88df48e61a792875a6b024490d73f/layers/x-25.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-26.webp",
+ "bytes": 2760,
+ "sha256": "4e4807c3a1fe8ca1978344545582b83ee2be935414f84a494651bd325d160e44",
+ "key": "runtime-assets/4e4807c3a1fe8ca1978344545582b83ee2be935414f84a494651bd325d160e44/layers/x-26.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-27.webp",
+ "bytes": 2544,
+ "sha256": "13f3e17314addea720fbf3a510ad949aa1649f3e8e08f7d642ca08cee3d3f7d2",
+ "key": "runtime-assets/13f3e17314addea720fbf3a510ad949aa1649f3e8e08f7d642ca08cee3d3f7d2/layers/x-27.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-28.webp",
+ "bytes": 2354,
+ "sha256": "dc91558ab3c06726f14ae9e27bef95954bbc3d3eef826338dc9ba9f0baf2d882",
+ "key": "runtime-assets/dc91558ab3c06726f14ae9e27bef95954bbc3d3eef826338dc9ba9f0baf2d882/layers/x-28.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-29.webp",
+ "bytes": 2250,
+ "sha256": "5e0806b77e73aeb2b906728a8038eabedfcddff7673898d194b91e9701fb9534",
+ "key": "runtime-assets/5e0806b77e73aeb2b906728a8038eabedfcddff7673898d194b91e9701fb9534/layers/x-29.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-30.webp",
+ "bytes": 1344,
+ "sha256": "8c45c2ed0f0c3d8ca0578109f13cbcb07a00d237cceae0c75ad7cbe2fca6bb38",
+ "key": "runtime-assets/8c45c2ed0f0c3d8ca0578109f13cbcb07a00d237cceae0c75ad7cbe2fca6bb38/layers/x-30.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-31.webp",
+ "bytes": 678,
+ "sha256": "73fcf60f7dd711a814bf9a706e74af30ee09de772a37aab9a3282bb36033b488",
+ "key": "runtime-assets/73fcf60f7dd711a814bf9a706e74af30ee09de772a37aab9a3282bb36033b488/layers/x-31.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-00.webp",
+ "bytes": 470,
+ "sha256": "36ada998ef860f0bc340bc7f44a37d873894738b66be115eaf47ccbd651a404b",
+ "key": "runtime-assets/36ada998ef860f0bc340bc7f44a37d873894738b66be115eaf47ccbd651a404b/layers/y-00.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-01.webp",
+ "bytes": 1532,
+ "sha256": "1415d833d90edc69c8d42f8ab40366f6f0079843018dc0c1ae169625b71f8fa9",
+ "key": "runtime-assets/1415d833d90edc69c8d42f8ab40366f6f0079843018dc0c1ae169625b71f8fa9/layers/y-01.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-02.webp",
+ "bytes": 2132,
+ "sha256": "e638e87f8e27fe0995b2b3819e53c823a2be3cbf3d63ec71a847765e8dbad83a",
+ "key": "runtime-assets/e638e87f8e27fe0995b2b3819e53c823a2be3cbf3d63ec71a847765e8dbad83a/layers/y-02.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-03.webp",
+ "bytes": 2412,
+ "sha256": "0582883b575abeec945ec89597123dc9bd8399f3162ace31c3ab2d9d520e0aa2",
+ "key": "runtime-assets/0582883b575abeec945ec89597123dc9bd8399f3162ace31c3ab2d9d520e0aa2/layers/y-03.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-04.webp",
+ "bytes": 2428,
+ "sha256": "2dc0ebec1569270450ed7ff8ab3bd39431e7e2677323fb3cc8f8267a4a956a4a",
+ "key": "runtime-assets/2dc0ebec1569270450ed7ff8ab3bd39431e7e2677323fb3cc8f8267a4a956a4a/layers/y-04.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-05.webp",
+ "bytes": 2624,
+ "sha256": "53b834cd8c5edcc015792ba6bb424514c566bb3bbb07818d53fed58f848e0182",
+ "key": "runtime-assets/53b834cd8c5edcc015792ba6bb424514c566bb3bbb07818d53fed58f848e0182/layers/y-05.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-06.webp",
+ "bytes": 3006,
+ "sha256": "17120a5e08a5f8ec1b2a7dbd8d7b02b6adc83cdb73ed725bcd1ac2da251782bc",
+ "key": "runtime-assets/17120a5e08a5f8ec1b2a7dbd8d7b02b6adc83cdb73ed725bcd1ac2da251782bc/layers/y-06.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-07.webp",
+ "bytes": 3122,
+ "sha256": "6f3232d965a361b3e30daf51f650c1a80cb2252b2f552d10ae2f8db39b680d04",
+ "key": "runtime-assets/6f3232d965a361b3e30daf51f650c1a80cb2252b2f552d10ae2f8db39b680d04/layers/y-07.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-08.webp",
+ "bytes": 3174,
+ "sha256": "3791a24601502544ba4822d5ca6580461db82b78ca22d293f1d96a97141ef949",
+ "key": "runtime-assets/3791a24601502544ba4822d5ca6580461db82b78ca22d293f1d96a97141ef949/layers/y-08.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-09.webp",
+ "bytes": 3214,
+ "sha256": "b6f854ab7c835c2c55e7e9e2ab6471524e7dff527e30b139956a31819035920e",
+ "key": "runtime-assets/b6f854ab7c835c2c55e7e9e2ab6471524e7dff527e30b139956a31819035920e/layers/y-09.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-10.webp",
+ "bytes": 3324,
+ "sha256": "9b6f007f5a8bd88c4ac2985bc43c42423b0ba57e8209922fe0a6923f5d6f118e",
+ "key": "runtime-assets/9b6f007f5a8bd88c4ac2985bc43c42423b0ba57e8209922fe0a6923f5d6f118e/layers/y-10.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-11.webp",
+ "bytes": 3626,
+ "sha256": "0ec70fbed41fd7ec7075b0e812a92d8407a84404ae661e2e4e135765ee886fb5",
+ "key": "runtime-assets/0ec70fbed41fd7ec7075b0e812a92d8407a84404ae661e2e4e135765ee886fb5/layers/y-11.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-12.webp",
+ "bytes": 3152,
+ "sha256": "0254ea3f4a2f37882c286ae33643fdb6bc71c0edde6c09862e91a3b6620c0db1",
+ "key": "runtime-assets/0254ea3f4a2f37882c286ae33643fdb6bc71c0edde6c09862e91a3b6620c0db1/layers/y-12.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-13.webp",
+ "bytes": 4162,
+ "sha256": "57d0614e8729b6aa65f26866bbf663ab8cd4e6f92864e93703747575e21427e8",
+ "key": "runtime-assets/57d0614e8729b6aa65f26866bbf663ab8cd4e6f92864e93703747575e21427e8/layers/y-13.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-14.webp",
+ "bytes": 4362,
+ "sha256": "9b1f4c8bbdb8b3470e3fd10064d79817895243b35a1fb602fd9e48dc76ef52da",
+ "key": "runtime-assets/9b1f4c8bbdb8b3470e3fd10064d79817895243b35a1fb602fd9e48dc76ef52da/layers/y-14.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-15.webp",
+ "bytes": 4368,
+ "sha256": "4573521f262444a362732200e30d7fd237fa62265abef54198525e97bbcb7794",
+ "key": "runtime-assets/4573521f262444a362732200e30d7fd237fa62265abef54198525e97bbcb7794/layers/y-15.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-16.webp",
+ "bytes": 4212,
+ "sha256": "4f55b1204884a4c7781ab26db1c78f60bbb5e8c8ac545fbad2cd7893fbce780e",
+ "key": "runtime-assets/4f55b1204884a4c7781ab26db1c78f60bbb5e8c8ac545fbad2cd7893fbce780e/layers/y-16.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-17.webp",
+ "bytes": 4270,
+ "sha256": "52b59cfd081f33e8d466669d3163f18b1dd90b16b00157e2e4a0908228e2a084",
+ "key": "runtime-assets/52b59cfd081f33e8d466669d3163f18b1dd90b16b00157e2e4a0908228e2a084/layers/y-17.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-18.webp",
+ "bytes": 4118,
+ "sha256": "c2e078d4be49d638fd955d4b736cade4e5ddd2e86b1dc945a9e81d668860868e",
+ "key": "runtime-assets/c2e078d4be49d638fd955d4b736cade4e5ddd2e86b1dc945a9e81d668860868e/layers/y-18.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-19.webp",
+ "bytes": 3336,
+ "sha256": "5843cf3cf18325a814cc278f8ea900c96fbeffbadab8d2fd0331a1d1029b4051",
+ "key": "runtime-assets/5843cf3cf18325a814cc278f8ea900c96fbeffbadab8d2fd0331a1d1029b4051/layers/y-19.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-20.webp",
+ "bytes": 4102,
+ "sha256": "068c6b6c59a9711478b32830a5c0b4b57125051dbce126320c512c6c7a6fed91",
+ "key": "runtime-assets/068c6b6c59a9711478b32830a5c0b4b57125051dbce126320c512c6c7a6fed91/layers/y-20.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-21.webp",
+ "bytes": 3492,
+ "sha256": "9eb898dd4b7270594b4e518f97522b4170479c38dcb82dcab90bae4975d3ca74",
+ "key": "runtime-assets/9eb898dd4b7270594b4e518f97522b4170479c38dcb82dcab90bae4975d3ca74/layers/y-21.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-22.webp",
+ "bytes": 2920,
+ "sha256": "9961c67624b32d763a9f1df1cfdcd756daee1b2242d00bc1248e594cb6354f1b",
+ "key": "runtime-assets/9961c67624b32d763a9f1df1cfdcd756daee1b2242d00bc1248e594cb6354f1b/layers/y-22.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-23.webp",
+ "bytes": 2512,
+ "sha256": "2853242227d6a97b992006bf773fdac3bb74ecac08a7ef5f3bff28d151eae590",
+ "key": "runtime-assets/2853242227d6a97b992006bf773fdac3bb74ecac08a7ef5f3bff28d151eae590/layers/y-23.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-24.webp",
+ "bytes": 2634,
+ "sha256": "743e0747fadff33bc77c4070d3a9b04fe15ecb46d500c96c5cdab9f47a89b6db",
+ "key": "runtime-assets/743e0747fadff33bc77c4070d3a9b04fe15ecb46d500c96c5cdab9f47a89b6db/layers/y-24.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-25.webp",
+ "bytes": 2186,
+ "sha256": "373df984de9b7ad0d6d95809a3299dfce704f2269ebb5435ac1665610218301d",
+ "key": "runtime-assets/373df984de9b7ad0d6d95809a3299dfce704f2269ebb5435ac1665610218301d/layers/y-25.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-26.webp",
+ "bytes": 2352,
+ "sha256": "5508cb54423a2d5cafc244aefc83d86ace0c03472b92066b39a1443c7d7ec154",
+ "key": "runtime-assets/5508cb54423a2d5cafc244aefc83d86ace0c03472b92066b39a1443c7d7ec154/layers/y-26.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-27.webp",
+ "bytes": 2372,
+ "sha256": "f4e7a820e0c094c7de27b35bfa9d19623f394984c5e0185a6cc19331f909fd99",
+ "key": "runtime-assets/f4e7a820e0c094c7de27b35bfa9d19623f394984c5e0185a6cc19331f909fd99/layers/y-27.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-28.webp",
+ "bytes": 2012,
+ "sha256": "13797069fdc06f06d82fa454f5da8fcdc02bf32deff81c72b7d678c0e4804262",
+ "key": "runtime-assets/13797069fdc06f06d82fa454f5da8fcdc02bf32deff81c72b7d678c0e4804262/layers/y-28.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-29.webp",
+ "bytes": 2010,
+ "sha256": "d82a866103e6874d481c56db887e626ff727024437f335d3fdeddcf08934c78a",
+ "key": "runtime-assets/d82a866103e6874d481c56db887e626ff727024437f335d3fdeddcf08934c78a/layers/y-29.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-30.webp",
+ "bytes": 1556,
+ "sha256": "e3ed8ddb23297696a321333d21e78c8733569e1e143c79a0cfb37130e42aebd3",
+ "key": "runtime-assets/e3ed8ddb23297696a321333d21e78c8733569e1e143c79a0cfb37130e42aebd3/layers/y-30.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "provenance.json",
+ "bytes": 37975,
+ "sha256": "24e8fb2b2e5a7d2ed514543b7f13f76b066a2eb859acf6ee54ab6df0ddb78981",
+ "key": "runtime-assets/24e8fb2b2e5a7d2ed514543b7f13f76b066a2eb859acf6ee54ab6df0ddb78981/provenance.json"
+ },
+ {
+ "id": "m33",
+ "filename": "presentation.json",
+ "bytes": 1799,
+ "sha256": "9193e89bf84c577cf77f8fb4d584505b03a069264f009959ece9df73c6c59d71",
+ "key": "runtime-assets/9193e89bf84c577cf77f8fb4d584505b03a069264f009959ece9df73c6c59d71/presentation.json"
+ },
+ {
+ "id": "m33",
+ "filename": "datasets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3.webp",
+ "bytes": 59744,
+ "sha256": "d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3",
+ "key": "runtime-assets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3/datasets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3.webp"
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "points.json",
+ "bytes": 345406,
+ "sha256": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee",
+ "key": "runtime-assets/595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee/points.json"
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "cloud.webp",
+ "bytes": 1706,
+ "sha256": "ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c",
+ "key": "runtime-assets/ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c/cloud.webp"
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "provenance.json",
+ "bytes": 10363,
+ "sha256": "25fd338e082b947f3071dd635f47ba133067a9a091550e61521b7527e95a107f",
+ "key": "runtime-assets/25fd338e082b947f3071dd635f47ba133067a9a091550e61521b7527e95a107f/provenance.json"
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "presentation.json",
+ "bytes": 490,
+ "sha256": "224e620caa689a95820bb15ad87bc37e7c7a6c2489db2136a441096246b10889",
+ "key": "runtime-assets/224e620caa689a95820bb15ad87bc37e7c7a6c2489db2136a441096246b10889/presentation.json"
+ }
+ ],
+ "requests": [
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353/layers/z-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137/layers/z-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596/layers/z-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4/layers/z-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05/layers/z-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35/layers/z-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01/layers/z-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35/layers/z-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93/layers/z-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4/layers/z-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3/layers/z-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f/layers/z-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572/layers/z-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911/layers/z-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433/layers/z-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284/layers/z-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284/layers/z-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3/layers/z-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433/layers/z-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572/layers/z-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911/layers/z-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93/layers/z-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f/layers/z-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4/layers/z-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35/layers/z-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4/layers/z-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/16951a0b2f8fcccdd5c9b7c419cd6e486fda759448437ffe06fc6ba20a3b3957/image-layers.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35/layers/z-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137/layers/z-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01/layers/z-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05/layers/z-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353/layers/z-31.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596/layers/z-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7da54ef71c1872bf2da0180a62f5d516a8f524b56da9405c3bf8c5a1c6b0009c/layers/z-detail.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ab3c45ef714a5e9e051f58cd1089d4c5d0d83c93d0ef5db193caab6e1a445d6e/layers/x-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e78eeeb4e4bff12c71779eb59ad753e333a202b46a8e6482932b8379509f0f5c/layers/x-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a973db4172b7d63477c0ca311ddd238f7f13fa3bbc6a1bdb8f4b1e0b9b11896a/layers/x-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d464be75e453dc4a95e26c7fb38c5f46e6a7a0eb37497dad32c3cfe0db95938f/layers/x-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d46f27c04493425a415a5d39e2a9e9acc2eb2f9b04682e3db83fad6dfe638e49/layers/x-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/01f47abf55059be7fd2647523fc87600e2d63834f8c7e70640b5711c15638c9b/layers/x-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2de4319c85aaab323cedaeedfc86bdab53db0bfc33019e6eeb3d8ccca2ae97f1/layers/x-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/eb854b16b3824e8e47f87f879e56808868f689e036cd4483dd01bd78457f83a8/layers/x-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e71c5a2506fdceedb650dd2babc5e73cc69fe4beea48278782749fd1840e5a6d/layers/x-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6da86dca8873f6d5bad23a049050ad14b0bb186484dba9d4e5f8601f95d9dcf6/layers/x-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/066d7e6d9b2057384cca5e78af641df790f5ccabdee90fe68b0ac793e3714267/layers/x-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f094f5781ece11c3306c3343bcf0267b3a77ba83f69aa4045ac222bcca5863c5/layers/x-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e55df8e13829273a44e7c03d2d210d3ee8ac8a444f3e4b90a52d497427260412/layers/x-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/add47432f770ba703f598efb70a50dac12af9d03ead2de75d3c1e9c878f91d30/layers/x-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/900b1aada73e64f398e8a0245f28ae7bf651740879f64c71f18ac604293e407d/layers/x-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/262b930cf8a41787c65a3b2f4089106f5a9d379e122f56067d4239a84d65e61e/layers/x-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3fae9a41364c44e2687c51fd3fd215a5af3ab59f46dce8b0a7fa1d8e80f1e76f/layers/x-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/886d478112b4f95449d46d239d253649c8c54ac652349ca8ddbed7436ac80a61/layers/x-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d4741e12d20490cc650b95df8c8b7e1ff2615f148be0a759592bd7684b566729/layers/x-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/15390b6c4c5c8cfd19c85857df4b9aaf80f0dd701c62ef665ad75c445cfd00ea/layers/x-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7229f90e841349a0e3040a7ba36cb8e6a6fd031786ba276d652e9966cca9b352/layers/x-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/012fa982feec9ee649988eefe9d359394c6266c67397c086afbc7bfecde4a3ea/layers/x-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/14fd79cc7ad12c1d9f7f9e730c0a5d928e8b4eede72216c4074a15b778ef45df/layers/x-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8806454f09e05a8242169fb729ffd62353ad6dec22e6372e20936ed6cd9afcb8/layers/x-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5275337df162438547ce51109b8825bc30c08a4b58e4e982945f343643d91058/layers/x-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/aaf60d06e5b0a7b9136f9aac1a518c1bf59c72b2d7734f5f4b74305a3c3bc3ea/layers/x-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1cb5d9e6b724c19e5800d8fe1a093cce2ed89146dff460952b703374491e43ee/layers/x-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d369ea41b48f54cde8701fa2ca046a1500658967a22e472af9671d5f07a611a9/layers/y-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7be4374bf82ad267a3412e7c318c13e86c03b6160fb7d3ad1ef1f10800af2bde/layers/x-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c0ecd45f8bce89e8bcb5b6b2b419e8d4bb2ce63967154a7a3cfe9a854e8a30c8/layers/y-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/670683e45cc76f09d2fabaa8c33d0de4f985e6a3e408dafbc9f42853c450ebae/layers/y-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ef21f88b9a5a3fe6caf0f5eb77dec87fa0e9fa776ef208ab3177f75b83d779ef/layers/y-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1877add88dec4803e86ad04981d6a616125cbb4a9b23828e10661ac0df3a29a4/layers/y-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ad8c4648010f0facc726cf6f64de2421045c05b50756876960bf550f6168e02e/layers/y-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c288ad201a5644b8521a5d9a20a67479b89d2bcd24354ab17b9f4ef67d845db3/layers/y-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b3129189d8e02ba191f73fdd6ef0f74a6fad646cabbc7df354205e8438b90b57/layers/y-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1d9665c4314ff93e641de2368f9ea887d2fe6f81c570a7f3a3606d4a952084e4/layers/y-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/dfafcb4e68bac5394285b3d7ea589ff43444f3732d76db17d363e59fa694d454/layers/y-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ec870ddaeaa7615f5d8ab0f5d46a78d320aaf9fc59b806e2ffa1df85a91057ec/layers/y-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/455eb90107ff596122362fb42d9f979f55a65734e9290eefd3a2888155999ce4/layers/y-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cfb523ee09f4f5a755910c5b86f3b8227ef46f6035eb2d5493eedcc84583c098/layers/y-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/872d95de54845c3768c0e638de78f8f43c6fc1f9bc233988bea64d53ca86d821/layers/y-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/334b0a6ce53fcdeff82133aeabba8286dbe71e1463915b9c8af55663b60170dc/layers/y-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3f368bbbe73ca3a4a9fdcf6aecd1413aee471aaf8eba6da51df7e4c7d997fe1f/layers/y-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/040001269ace310d7afc32e4dffb240878009d3d033b64b399c0c49c720bb2f6/layers/y-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c895666cb91769fca707167be46d0ba6f3285e295aa0cf06afcb734bf775b3be/layers/y-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0ca061c1e4cb504647e15696f74fde84b5f3a9685f62562a0342536c2ca724b4/layers/y-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/69e82473c09d985ee2b9d163c9ff249f0bca837b0b47834575435e2b05edcf6f/layers/y-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7f427707bf01b3682239f90b165a3811617fd97dfab59b4f18903402a643b615/layers/y-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c3d435478a2f011e0f09d0a99293cfcdb88b57ffbb1613635999478bf4b50bbf/layers/y-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ced11e3d3bd1f5ef96646ce2f7b7f7e8f54047e57398fda77c8d24146659b429/layers/y-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/262b3347921ae8a48a60a2395616aeeeca477312bdc8e4343d9531b392b8c711/layers/y-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1ab16b4007a25fcc92b58788f7be38c3e928e5263bf940f5d3e183cab2d88a57/layers/y-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e198fb0a565b3402bf3789a166d618cd645f736600158378d2ca73e112300976/layers/y-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/bdb46824a992a8cd31c9c91cd6bc0987d390359762f70eb12c194d17288985f2/layers/y-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0764e92999e6a776fd15124558847e763a0bf585e3fe33fa36fe90ddaf7c27ed/layers/y-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ad902a30624a2659ab0e680739684698a3e1348305e6ee0920f879dd9a04fdb7/layers/y-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/60da9e0b67cf85fbec7fbab7530827fdc874a0443e225acfd9d21ba7e98357ab/layers/y-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/61303a62465dd706e6f1ddb23dba2c16c89d0493e67c16b67f38e890242cb4f3/layers/y-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9/datasets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe/layers/z-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd/layers/z-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f/layers/z-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8/layers/z-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb/layers/z-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c/layers/z-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4/layers/z-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d/layers/z-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67/layers/z-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e/layers/z-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b/layers/z-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee/layers/z-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac/layers/z-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b/layers/z-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8/layers/z-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3/layers/z-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ef01151d319c4a1ad480d65a58cafaf24db1b3d28ac5deab5bcb0a420d36dbe1/presentation.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8fd017ce96e753da0d6fd53ed40040705e56c1a436605d6cd90bdecd98212404/provenance.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1e89f7d7ea9673eeebbbc3ad6da2695202dce3dfd2281830aa5fa5ff1c9e6572/image-layers.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3/layers/z-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8/layers/z-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac/layers/z-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b/layers/z-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee/layers/z-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b/layers/z-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e/layers/z-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d/layers/z-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67/layers/z-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4/layers/z-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c/layers/z-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb/layers/z-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8/layers/z-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f/layers/z-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd/layers/z-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/683c13ef47fe913904b73282ecc98e5a2577e3a4a616d4ac1f4f737e5b3e8c31/layers/z-detail.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe/layers/z-31.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/35d5dc3ad1a75292bb9da6897e37d85c72a04c7c7c929dd157999a1c3f62731a/layers/x-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8116cd8f718623d7a5c64780852f43743d77e6cb502015188015734b62a77120/layers/x-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7a039f59b82b66140d7846e6c42af9610e5d1528d0a5753764038d076c6070ef/layers/x-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/140005f9292e15140049210e50907fdac8f25c1d23ae5cf53420ba0076312931/layers/x-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/72c8864fee520d58bc55486580be019ad22235f9613d4b650aa714ce8455d5c9/layers/x-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/56868d53d8504d62f6d128d97ed8e64f28bf47014b6e61d430d241ba37ca0766/layers/x-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9da3c0bf6600982ba8311694c4a3720f050f172d244e917302b07ec70226e146/layers/x-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f20ac9fd2dd466910602c93e5e929c30735717bf8b292ee05e818374a19120d7/layers/x-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3b081a11d1da314987c4c68de6fee632676d88751610f77bafb6b4a66130ce40/layers/x-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b67d875f5733c94041882ff384aa090a2aaf6ace1a6bb739d5732fe81862fa86/layers/x-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a4aa9ab2e15cd8ff874bcecbcaa1ada6503981efbe3808d9aeefbd0a1bdc4f10/layers/x-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a57aa43aa5f8edf7f45ace919e65bfb31c8ecffe49d7e09458f6f8ea271a4ac2/layers/x-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f77aab646f679802c1d539bf9698cbb70a9f48e36358a0e8ca7fb0f081ff2b0f/layers/x-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5d69273f953fa930eebed5d89e71bf2194ee4557ce8cce50a0f0aff166b282cf/layers/x-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/faf9f6f168641a7afea3de68bdc6d0083f7a3f82941aaaa2900e6b194ce598e5/layers/x-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e39d0c714ffbd3e62c237a5abeee06fd33cacaa2563370f95cd6cc8302e82e43/layers/x-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d92a95e7a7f0feacdb100ca9b921f0ae88e63826e499df33c361291aa493a986/layers/x-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2fd1ecf0fe47d904eda3b46f8249771544b4cf60c25b851352be383b6adc486d/layers/x-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0ec312513b4cbae6c094f930f2dfe5fc42237ea88a37b2f34dd16e0ea37a0a54/layers/x-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/531f166a3156312adfd5ab1ce56b1853e405af05f0f9d80a75d9a9939177276b/layers/x-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b694f6808551b3ed520c6979bb19b3d6ec36358c16011e13153ba09721d5ee1a/layers/x-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/901a31717542c9214518cff2a3901ec8e7bb1e0a423df020704691738eecc92c/layers/x-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ef8a1f0cc3267af10d6c294b6c562da9738fa055a0f8d726505644715112d110/layers/x-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9e81aabfc770c9d936458e061c6c88bb6a12a0de28ffd7752432b0b87aa7b8a5/layers/x-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d1421dc8b2d2d166c6fbda6dab3c6dd88be88df48e61a792875a6b024490d73f/layers/x-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6075a88ffaa26776ba0ffa8f1bd7247e4902548a2684ad85dacd18c6590d893c/layers/x-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4e4807c3a1fe8ca1978344545582b83ee2be935414f84a494651bd325d160e44/layers/x-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/13f3e17314addea720fbf3a510ad949aa1649f3e8e08f7d642ca08cee3d3f7d2/layers/x-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/dc91558ab3c06726f14ae9e27bef95954bbc3d3eef826338dc9ba9f0baf2d882/layers/x-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5e0806b77e73aeb2b906728a8038eabedfcddff7673898d194b91e9701fb9534/layers/x-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/73fcf60f7dd711a814bf9a706e74af30ee09de772a37aab9a3282bb36033b488/layers/x-31.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8c45c2ed0f0c3d8ca0578109f13cbcb07a00d237cceae0c75ad7cbe2fca6bb38/layers/x-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/36ada998ef860f0bc340bc7f44a37d873894738b66be115eaf47ccbd651a404b/layers/y-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1415d833d90edc69c8d42f8ab40366f6f0079843018dc0c1ae169625b71f8fa9/layers/y-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e638e87f8e27fe0995b2b3819e53c823a2be3cbf3d63ec71a847765e8dbad83a/layers/y-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0582883b575abeec945ec89597123dc9bd8399f3162ace31c3ab2d9d520e0aa2/layers/y-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/17120a5e08a5f8ec1b2a7dbd8d7b02b6adc83cdb73ed725bcd1ac2da251782bc/layers/y-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6f3232d965a361b3e30daf51f650c1a80cb2252b2f552d10ae2f8db39b680d04/layers/y-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/53b834cd8c5edcc015792ba6bb424514c566bb3bbb07818d53fed58f848e0182/layers/y-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2dc0ebec1569270450ed7ff8ab3bd39431e7e2677323fb3cc8f8267a4a956a4a/layers/y-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3791a24601502544ba4822d5ca6580461db82b78ca22d293f1d96a97141ef949/layers/y-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9b6f007f5a8bd88c4ac2985bc43c42423b0ba57e8209922fe0a6923f5d6f118e/layers/y-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b6f854ab7c835c2c55e7e9e2ab6471524e7dff527e30b139956a31819035920e/layers/y-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0ec70fbed41fd7ec7075b0e812a92d8407a84404ae661e2e4e135765ee886fb5/layers/y-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/57d0614e8729b6aa65f26866bbf663ab8cd4e6f92864e93703747575e21427e8/layers/y-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9b1f4c8bbdb8b3470e3fd10064d79817895243b35a1fb602fd9e48dc76ef52da/layers/y-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0254ea3f4a2f37882c286ae33643fdb6bc71c0edde6c09862e91a3b6620c0db1/layers/y-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4573521f262444a362732200e30d7fd237fa62265abef54198525e97bbcb7794/layers/y-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4f55b1204884a4c7781ab26db1c78f60bbb5e8c8ac545fbad2cd7893fbce780e/layers/y-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c2e078d4be49d638fd955d4b736cade4e5ddd2e86b1dc945a9e81d668860868e/layers/y-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/52b59cfd081f33e8d466669d3163f18b1dd90b16b00157e2e4a0908228e2a084/layers/y-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9eb898dd4b7270594b4e518f97522b4170479c38dcb82dcab90bae4975d3ca74/layers/y-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9961c67624b32d763a9f1df1cfdcd756daee1b2242d00bc1248e594cb6354f1b/layers/y-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5843cf3cf18325a814cc278f8ea900c96fbeffbadab8d2fd0331a1d1029b4051/layers/y-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/068c6b6c59a9711478b32830a5c0b4b57125051dbce126320c512c6c7a6fed91/layers/y-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2853242227d6a97b992006bf773fdac3bb74ecac08a7ef5f3bff28d151eae590/layers/y-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/743e0747fadff33bc77c4070d3a9b04fe15ecb46d500c96c5cdab9f47a89b6db/layers/y-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5508cb54423a2d5cafc244aefc83d86ace0c03472b92066b39a1443c7d7ec154/layers/y-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/373df984de9b7ad0d6d95809a3299dfce704f2269ebb5435ac1665610218301d/layers/y-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f4e7a820e0c094c7de27b35bfa9d19623f394984c5e0185a6cc19331f909fd99/layers/y-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/13797069fdc06f06d82fa454f5da8fcdc02bf32deff81c72b7d678c0e4804262/layers/y-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e3ed8ddb23297696a321333d21e78c8733569e1e143c79a0cfb37130e42aebd3/layers/y-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d82a866103e6874d481c56db887e626ff727024437f335d3fdeddcf08934c78a/layers/y-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c/cloud.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3/datasets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/24e8fb2b2e5a7d2ed514543b7f13f76b066a2eb859acf6ee54ab6df0ddb78981/provenance.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9193e89bf84c577cf77f8fb4d584505b03a069264f009959ece9df73c6c59d71/presentation.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee/points.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/25fd338e082b947f3071dd635f47ba133067a9a091550e61521b7527e95a107f/provenance.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/224e620caa689a95820bb15ad87bc37e7c7a6c2489db2136a441096246b10889/presentation.json",
+ "status": 200
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/front.png b/src/objects/omega-centauri/evidence/2026-09-20/front.png
new file mode 100644
index 0000000000..5b176a8aa7
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-20/front.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/native-arrival.png b/src/objects/omega-centauri/evidence/2026-09-20/native-arrival.png
new file mode 100644
index 0000000000..04212094eb
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-20/native-arrival.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/native-focus.json b/src/objects/omega-centauri/evidence/2026-09-20/native-focus.json
new file mode 100644
index 0000000000..34e090572d
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/native-focus.json
@@ -0,0 +1,286 @@
+{
+ "base": "http://127.0.0.1:4219",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "browser": "148.0.7778.96",
+ "inputPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ }
+ ],
+ "errors": [],
+ "failed": [],
+ "cold": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "coldContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "warm": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "dragged": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -107690829656048200000,
+ -41068870101575090000,
+ -115681989469650910000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ -4.3973168737309055e-19,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 8165459600736778000,
+ "distanceRadii": 6.096653162046747,
+ "projectedRadiusPixels": 204.55157392967632
+ },
+ "zoomed": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105161342581436300000,
+ -41827716223958670000,
+ -123337799964711390000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ -4.3973168737309055e-19,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 66966738829483176,
+ "distanceRadii": 0.049999999999986326,
+ "projectedRadiusPixels": 24941.60000000682
+ },
+ "reselected": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -107690829656048270000,
+ -41068870101575070000,
+ -115681989469650940000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ 0,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 8165459600736769000,
+ "distanceRadii": 6.0966531620467395,
+ "projectedRadiusPixels": 204.55157392967655
+ },
+ "reselectedContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "zoomedOut": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -146973272045458080000,
+ -29284137384752270000,
+ 3211258784085815300
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ 0,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 133933478928421720000,
+ "distanceRadii": 100.00000094779793,
+ "projectedRadiusPixels": 12.470799881802016
+ },
+ "farContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 0.464877
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 0.999838
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0740891
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 0.999838
+ }
+ ],
+ "overview": [
+ {
+ "display": "block",
+ "opacity": 0.534735
+ }
+ ]
+ },
+ "returnedContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ },
+ {
+ "id": "m8",
+ "display": "none",
+ "opacity": 0
+ },
+ {
+ "id": "smc",
+ "display": "none",
+ "opacity": 0
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "rendering": {
+ "totalElements": 23209,
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": "1",
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "m8",
+ "display": "none",
+ "opacity": "0",
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "none",
+ "opacity": "0",
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "galaxyOverview": [
+ {
+ "opacity": "1",
+ "display": "none",
+ "elements": 1382
+ }
+ ]
+ },
+ "result": "passed"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/nearby-recomputation.json b/src/objects/omega-centauri/evidence/2026-09-20/nearby-recomputation.json
new file mode 100644
index 0000000000..11fa8d5c34
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/nearby-recomputation.json
@@ -0,0 +1,71 @@
+{
+ "points.json": {
+ "oldHash": "c70262ba6e3ae288da6acd29971d1b8da0eeb33ae8a924fcfa39ff9b2eceb8d3",
+ "newHash": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee",
+ "oldBytes": 345401,
+ "newBytes": 345406,
+ "count": 1454,
+ "numericCount": 1454,
+ "counts": {
+ "clouds": 1062,
+ "points": 392
+ },
+ "maxAbsolute": {
+ "path": "/clouds/151/covariance/4",
+ "old": 320.49881983457976,
+ "new": 320.4988198345795,
+ "delta": -2.8421709430404007e-13,
+ "relative": -8.867960713575609e-16
+ },
+ "maxRelative": {
+ "path": "/clouds/73/covariance/3",
+ "old": 0.08007690965625668,
+ "new": 0.08007690965629367,
+ "delta": 3.699818229563334e-14,
+ "relative": 4.620330936153022e-13
+ },
+ "sourceIdentical": true,
+ "frameIdentical": true
+ },
+ "provenance.json": {
+ "oldHash": "ee12abfd6a352def90493a13ade2c8297f6b75694240690980816e3482b582ea",
+ "newHash": "25fd338e082b947f3071dd635f47ba133067a9a091550e61521b7527e95a107f",
+ "oldBytes": 10363,
+ "newBytes": 10363,
+ "count": 2,
+ "numericCount": 1,
+ "counts": {
+ "products": 2
+ },
+ "maxAbsolute": {
+ "path": "/products/0/outputs/0/bytes",
+ "old": 345401,
+ "new": 345406,
+ "delta": 5,
+ "relative": 0.000014475927979363118
+ },
+ "maxRelative": {
+ "path": "/products/0/outputs/0/bytes",
+ "old": 345401,
+ "new": 345406,
+ "delta": 5,
+ "relative": 0.000014475927979363118
+ },
+ "differences": [
+ {
+ "path": "/products/0/outputs/0/sha256",
+ "old": "c70262ba6e3ae288da6acd29971d1b8da0eeb33ae8a924fcfa39ff9b2eceb8d3",
+ "new": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee"
+ },
+ {
+ "path": "/products/0/outputs/0/bytes",
+ "old": 345401,
+ "new": 345406,
+ "delta": 5,
+ "relative": 0.000014475927979363118
+ }
+ ],
+ "sourceIdentical": true,
+ "frameIdentical": true
+ }
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/oblique.png b/src/objects/omega-centauri/evidence/2026-09-20/oblique.png
new file mode 100644
index 0000000000..60d76b51d6
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-20/oblique.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/performance-after.json b/src/objects/omega-centauri/evidence/2026-09-20/performance-after.json
new file mode 100644
index 0000000000..79cfafb142
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/performance-after.json
@@ -0,0 +1,1134 @@
+{
+ "claim": "Actual shared close-up context policy, same native-focus baseline protocol, not user FPS",
+ "base": "http://127.0.0.1:4219",
+ "measuredAt": "2026-09-20T21:39:50.350Z",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "deviceScaleFactor": 1,
+ "browser": "148.0.7778.96",
+ "headless": true,
+ "hardware": {
+ "gpu": {
+ "devices": [
+ {
+ "vendorId": 65535,
+ "deviceId": 65535,
+ "vendorString": "Google Inc. (Google)",
+ "deviceString": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (LLVM 10.0.0) (0x0000C0DE)), SwiftShader driver-5.0.0)",
+ "driverVendor": "SwANGLE",
+ "driverVersion": "5.0.0"
+ }
+ ],
+ "auxAttributes": {
+ "amdSwitchable": false,
+ "canSupportThreadedTextureMailbox": false,
+ "directRenderingVersion": "",
+ "displayType": "ANGLE_SWIFTSHADER",
+ "glExtensions": "GL_AMD_performance_monitor GL_ANGLE_base_vertex_base_instance_shader_builtin GL_ANGLE_blob_cache GL_ANGLE_client_arrays GL_ANGLE_clip_cull_distance GL_ANGLE_compressed_texture_etc GL_ANGLE_copy_texture_3d GL_ANGLE_depth_texture GL_ANGLE_framebuffer_blit GL_ANGLE_framebuffer_multisample GL_ANGLE_get_image GL_ANGLE_get_serialized_context_string GL_ANGLE_get_tex_level_parameter GL_ANGLE_instanced_arrays GL_ANGLE_memory_object_flags GL_ANGLE_memory_size GL_ANGLE_multi_draw GL_ANGLE_pack_reverse_row_order GL_ANGLE_polygon_mode GL_ANGLE_program_binary_readiness_query GL_ANGLE_program_cache_control GL_ANGLE_read_only_depth_stencil_feedback_loops GL_ANGLE_relaxed_vertex_attribute_type GL_ANGLE_renderability_validation GL_ANGLE_request_extension GL_ANGLE_rgbx_internal_format GL_ANGLE_robust_client_memory GL_ANGLE_robust_fragment_shader_output GL_ANGLE_robust_resource_initialization GL_ANGLE_shader_pixel_local_storage GL_ANGLE_shader_pixel_local_storage_coherent GL_ANGLE_stencil_texturing GL_ANGLE_texture_compression_dxt3 GL_ANGLE_texture_compression_dxt5 GL_ANGLE_texture_multisample GL_ANGLE_texture_usage GL_ANGLE_translated_shader_source GL_ANGLE_vulkan_image GL_ANGLE_yuv_internal_format GL_APPLE_clip_distance GL_ARM_rgba8 GL_ARM_shader_framebuffer_fetch GL_ARM_shader_framebuffer_fetch_depth_stencil GL_CHROMIUM_bind_generates_resource GL_CHROMIUM_bind_uniform_location GL_CHROMIUM_copy_compressed_texture GL_CHROMIUM_copy_texture GL_CHROMIUM_lose_context GL_EXT_EGL_image_array GL_EXT_EGL_image_external_wrap_modes GL_EXT_EGL_image_storage GL_EXT_base_instance GL_EXT_blend_minmax GL_EXT_buffer_storage GL_EXT_clear_texture GL_EXT_clip_control GL_EXT_clip_cull_distance GL_EXT_color_buffer_float GL_EXT_color_buffer_half_float GL_EXT_compressed_ETC1_RGB8_sub_texture GL_EXT_conservative_depth GL_EXT_copy_image GL_EXT_debug_label GL_EXT_debug_marker GL_EXT_depth_clamp GL_EXT_discard_framebuffer GL_EXT_disjoint_timer_query GL_EXT_draw_buffers GL_EXT_draw_buffers_indexed GL_EXT_draw_elements_base_vertex GL_EXT_float_blend GL_EXT_frag_depth GL_EXT_instanced_arrays GL_EXT_map_buffer_range GL_EXT_memory_object GL_EXT_memory_object_fd GL_EXT_occlusion_query_boolean GL_EXT_polygon_offset_clamp GL_EXT_read_format_bgra GL_EXT_robustness GL_EXT_sRGB GL_EXT_sRGB_write_control GL_EXT_semaphore GL_EXT_separate_shader_objects GL_EXT_shader_framebuffer_fetch GL_EXT_shader_framebuffer_fetch_non_coherent GL_EXT_shader_non_constant_global_initializers GL_EXT_shader_texture_lod GL_EXT_shadow_samplers GL_EXT_texture_border_clamp GL_EXT_texture_compression_bptc GL_EXT_texture_compression_dxt1 GL_EXT_texture_compression_rgtc GL_EXT_texture_compression_s3tc_srgb GL_EXT_texture_cube_map_array GL_EXT_texture_filter_anisotropic GL_EXT_texture_format_BGRA8888 GL_EXT_texture_mirror_clamp_to_edge GL_EXT_texture_rg GL_EXT_texture_sRGB_R8 GL_EXT_texture_sRGB_RG8 GL_EXT_texture_sRGB_decode GL_EXT_texture_shadow_lod GL_EXT_texture_storage GL_EXT_texture_type_2_10_10_10_REV GL_EXT_unpack_subimage GL_KHR_blend_equation_advanced GL_KHR_debug GL_KHR_robust_buffer_access_behavior GL_KHR_robustness GL_KHR_texture_compression_astc_ldr GL_NV_depth_buffer_float2 GL_NV_fence GL_NV_framebuffer_blit GL_NV_pack_subimage GL_NV_pixel_buffer_object GL_NV_polygon_mode GL_NV_read_depth GL_NV_read_depth_stencil GL_NV_read_stencil GL_NV_shader_noperspective_interpolation GL_OES_EGL_image GL_OES_EGL_image_external GL_OES_EGL_image_external_essl3 GL_OES_EGL_sync GL_OES_compressed_EAC_R11_signed_texture GL_OES_compressed_EAC_R11_unsigned_texture GL_OES_compressed_EAC_RG11_signed_texture GL_OES_compressed_EAC_RG11_unsigned_texture GL_OES_compressed_ETC1_RGB8_texture GL_OES_compressed_ETC2_RGB8_texture GL_OES_compressed_ETC2_RGBA8_texture GL_OES_compressed_ETC2_punchthroughA_RGBA8_texture GL_OES_compressed_ETC2_punchthroughA_sRGB8_alpha_texture GL_OES_compressed_ETC2_sRGB8_alpha8_texture GL_OES_compressed_ETC2_sRGB8_texture GL_OES_compressed_paletted_texture GL_OES_copy_image GL_OES_depth24 GL_OES_depth32 GL_OES_depth_texture GL_OES_depth_texture_cube_map GL_OES_draw_buffers_indexed GL_OES_draw_elements_base_vertex GL_OES_element_index_uint GL_OES_fbo_render_mipmap GL_OES_get_program_binary GL_OES_mapbuffer GL_OES_packed_depth_stencil GL_OES_required_internalformat GL_OES_rgb8_rgba8 GL_OES_sample_shading GL_OES_sample_variables GL_OES_shader_multisample_interpolation GL_OES_standard_derivatives GL_OES_surfaceless_context GL_OES_texture_3D GL_OES_texture_border_clamp GL_OES_texture_cube_map_array GL_OES_texture_float GL_OES_texture_float_linear GL_OES_texture_half_float GL_OES_texture_half_float_linear GL_OES_texture_npot GL_OES_texture_stencil8 GL_OES_texture_storage_multisample_2d_array GL_OES_vertex_array_object GL_OES_vertex_half_float GL_OES_vertex_type_10_10_10_2 GL_OVR_multiview GL_OVR_multiview2 ",
+ "glImplementationParts": "(gl=egl-angle,angle=swiftshader)",
+ "glRenderer": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (LLVM 10.0.0) (0x0000C0DE)), SwiftShader driver-5.0.0)",
+ "glResetNotificationStrategy": 33362,
+ "glVendor": "Google Inc. (Google)",
+ "glVersion": "OpenGL ES 3.0 (ANGLE 2.1.27471 git hash: cc0e3572e878)",
+ "glWsExtensions": "",
+ "glWsVendor": "",
+ "glWsVersion": "",
+ "inProcessGpu": false,
+ "initializationTime": 0.025055,
+ "isAsan": false,
+ "isClangCoverage": false,
+ "jpegDecodeAcceleratorSupported": false,
+ "maxMsaaSamples": "4",
+ "optimus": false,
+ "passthroughCmdDecoder": true,
+ "pixelShaderVersion": "3.00",
+ "processCrashCount": 0,
+ "sandboxed": false,
+ "skiaBackendType": "GraphiteDawnVulkan",
+ "subpixelFontRendering": true,
+ "targetCpuBits": 64,
+ "vertexShaderVersion": "3.00",
+ "visibilityCallbackCallCount": 0
+ },
+ "featureStatus": {
+ "2d_canvas": "unavailable_software",
+ "direct_rendering_display_compositor": "disabled_off_ok",
+ "gpu_compositing": "disabled_software",
+ "multiple_raster_threads": "enabled_on",
+ "opengl": "disabled_off",
+ "rasterization": "disabled_software",
+ "raw_draw": "disabled_off_ok",
+ "skia_graphite": "disabled_off",
+ "trees_in_viz": "disabled_off",
+ "video_decode": "disabled_software",
+ "video_encode": "disabled_software",
+ "webgl": "unavailable_software",
+ "webgpu": "unavailable_software",
+ "webnn": "unavailable_software"
+ },
+ "driverBugWorkarounds": [],
+ "videoDecoding": [],
+ "videoEncoding": []
+ }
+ },
+ "node": "v22.23.2",
+ "platform": "darwin",
+ "arch": "arm64",
+ "protocol": {
+ "nativeUrl": "/sun/?focus=omega-centauri",
+ "warmup": "networkidle, then30 RAF",
+ "idleFrames": 30,
+ "drag": {
+ "from": [
+ 720,
+ 500
+ ],
+ "to": [
+ 920,
+ 560
+ ],
+ "steps": 40
+ },
+ "settleFrames": 30,
+ "noCameraOverride": true,
+ "noSourceChanges": true
+ },
+ "inputPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ },
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "bytes": 13264,
+ "sha256": "67286a0589f42d990d467d5c1e199661bc8b98b7102d152c527f34fc095e4a36"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-universe-runtime.ts",
+ "bytes": 48720,
+ "sha256": "fd0d735b8f44cd639e3c9cf5333a4f4fbef1a2c9dbbbf8d288cf59a4441dae2f"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.ts",
+ "bytes": 873,
+ "sha256": "12a727775a8277fede7cd743642b960a30c10a7334f2e47186d97b6e8d5413f4"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-world-context.ts",
+ "bytes": 75841,
+ "sha256": "00758c8d75054092c209c68b4e311605a4a886d59fda5b36cdf86bf5d41d9ab2"
+ },
+ {
+ "path": "src/renderers/css/dist/universe.js",
+ "bytes": 10185,
+ "sha256": "a4aa3371c6836dcb1df973ab5cadbf9c9bd5d3ebb2555e9e46aff31fc2b9c520"
+ }
+ ],
+ "reports": [
+ {
+ "repeat": 1,
+ "beforePose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "afterPose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -109962411235333930000,
+ -40387395627789610000,
+ -116972221966195500000
+ ],
+ "orientationXyzw": [
+ -0.0937021412901332,
+ -0.3123404709671109,
+ -5.5050914713649234e-18,
+ 0.9453376851229868
+ ]
+ }
+ },
+ "distanceM": 8165459600736674000,
+ "distanceRadii": 6.096653162046668,
+ "projectedRadiusPixels": 204.55157392967894
+ },
+ "beforeDom": {
+ "totalElements": 18068,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 78,
+ "eagerImagesPending": 0
+ },
+ "afterDom": {
+ "totalElements": 18068,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 78,
+ "eagerImagesPending": 0
+ },
+ "inputDurationMs": 8192.600583,
+ "idle": {
+ "durationMs": 249.40000009536743,
+ "raf": {
+ "frames": 29,
+ "medianGapMs": 8.300000000000182,
+ "p95GapMs": 9.200000000000728,
+ "maxGapMs": 9.599999999999454,
+ "gapsOver33ms": 0,
+ "gapsOver50ms": 0,
+ "gapsMs": [
+ 8.399999999999636,
+ 8.199999999999818,
+ 8.5,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.299999999999272,
+ 8.400000000000546,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.300000000000182,
+ 8.300000000000182,
+ 7.599999999999454,
+ 9.200000000000728,
+ 8.099999999999454,
+ 8.400000000000546,
+ 7.199999999999818,
+ 9.599999999999454,
+ 8.200000000000728,
+ 8.399999999999636,
+ 8.300000000000182,
+ 8.300000000000182,
+ 7.899999999999636,
+ 8.899999999999636
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 0,
+ "RecalcStyleCount": 0,
+ "LayoutDuration": 0,
+ "RecalcStyleDuration": 0,
+ "DevToolsCommandDuration": 0.00009400000000000033,
+ "ScriptDuration": 0.00033299999999999996,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.00538100000000008,
+ "TaskOtherDuration": 0.004954000000000014
+ }
+ },
+ "interaction": {
+ "durationMs": 9416.5,
+ "raf": {
+ "frames": 72,
+ "medianGapMs": 182.60000000000036,
+ "p95GapMs": 309.5,
+ "maxGapMs": 359.60000000000036,
+ "gapsOver33ms": 42,
+ "gapsOver50ms": 42,
+ "gapsMs": [
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.299999999999272,
+ 6.800000000000182,
+ 10.300000000000182,
+ 315.10000000000036,
+ 309.5,
+ 299.89999999999964,
+ 308.39999999999964,
+ 299,
+ 309.40000000000055,
+ 300,
+ 299.39999999999964,
+ 308.90000000000055,
+ 300,
+ 183.5,
+ 183.19999999999982,
+ 183.30000000000018,
+ 183.39999999999964,
+ 182.60000000000036,
+ 183.1999999999989,
+ 16.30000000000109,
+ 359.60000000000036,
+ 74.89999999999964,
+ 24.5,
+ 358.7999999999993,
+ 91.70000000000073,
+ 191.6999999999989,
+ 191.70000000000073,
+ 182,
+ 192.70000000000073,
+ 190.89999999999964,
+ 192.6999999999989,
+ 190.60000000000036,
+ 192,
+ 192.5,
+ 199.89999999999964,
+ 191.60000000000036,
+ 191.60000000000036,
+ 191.79999999999927,
+ 191.60000000000036,
+ 192,
+ 199.70000000000073,
+ 192,
+ 191.5,
+ 150,
+ 200.1999999999989,
+ 140.3000000000011,
+ 151,
+ 8.399999999999636,
+ 8.299999999999272,
+ 8.400000000001455,
+ 8.199999999998909,
+ 8.399999999999636,
+ 8.300000000001091,
+ 8.299999999999272,
+ 8.399999999999636,
+ 8.400000000001455,
+ 8.299999999999272,
+ 8.299999999999272,
+ 8.400000000001455,
+ 8.199999999998909,
+ 8.399999999999636,
+ 8.400000000001455,
+ 8.299999999999272,
+ 8.299999999999272,
+ 8.300000000001091,
+ 8.299999999999272,
+ 8.399999999999636,
+ 8.300000000001091,
+ 8.299999999999272,
+ 8.399999999999636
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 1,
+ "RecalcStyleCount": 47,
+ "LayoutDuration": 0.00005599999999999876,
+ "RecalcStyleDuration": 0.012311000000000002,
+ "DevToolsCommandDuration": 0.006171999999999999,
+ "ScriptDuration": 0.07597899999999999,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.16077200000000003,
+ "TaskOtherDuration": 0.06625400000000004
+ }
+ },
+ "cdpBefore": {
+ "Timestamp": 1845492.071557,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5259,
+ "LayoutObjects": 5798,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 27651,
+ "Resources": 143,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 27,
+ "LayoutCount": 12,
+ "RecalcStyleCount": 21,
+ "LayoutDuration": 0.010349,
+ "RecalcStyleDuration": 0.036433,
+ "DevToolsCommandDuration": 0.013875,
+ "ScriptDuration": 0.06126,
+ "V8CompileDuration": 0.000057,
+ "TaskDuration": 0.813889,
+ "TaskOtherDuration": 0.691915,
+ "ThreadTime": 0.564984,
+ "ProcessTime": 2.714837,
+ "JSHeapUsedSize": 203709284,
+ "JSHeapTotalSize": 262930432,
+ "FirstMeaningfulPaint": 1845490.287472,
+ "DomContentLoaded": 1845487.75536,
+ "NavigationStart": 1845487.345357
+ },
+ "cdpAfter": {
+ "Timestamp": 1845501.486495,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5422,
+ "LayoutObjects": 5798,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 27651,
+ "Resources": 143,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 145,
+ "LayoutCount": 13,
+ "RecalcStyleCount": 68,
+ "LayoutDuration": 0.010405,
+ "RecalcStyleDuration": 0.048744,
+ "DevToolsCommandDuration": 0.020047,
+ "ScriptDuration": 0.137239,
+ "V8CompileDuration": 0.000057,
+ "TaskDuration": 0.974661,
+ "TaskOtherDuration": 0.758169,
+ "ThreadTime": 0.711158,
+ "ProcessTime": 3.016536,
+ "JSHeapUsedSize": 221103740,
+ "JSHeapTotalSize": 264765440,
+ "FirstMeaningfulPaint": 1845490.287472,
+ "DomContentLoaded": 1845487.75536,
+ "NavigationStart": 1845487.345357
+ },
+ "errors": [],
+ "failed": []
+ },
+ {
+ "repeat": 2,
+ "beforePose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "afterPose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -109962411235333930000,
+ -40387395627789610000,
+ -116972221966195500000
+ ],
+ "orientationXyzw": [
+ -0.0937021412901332,
+ -0.3123404709671109,
+ -5.5050914713649234e-18,
+ 0.9453376851229868
+ ]
+ }
+ },
+ "distanceM": 8165459600736674000,
+ "distanceRadii": 6.096653162046668,
+ "projectedRadiusPixels": 204.55157392967894
+ },
+ "beforeDom": {
+ "totalElements": 18068,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 78,
+ "eagerImagesPending": 0
+ },
+ "afterDom": {
+ "totalElements": 18068,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 78,
+ "eagerImagesPending": 0
+ },
+ "inputDurationMs": 8131.747500000001,
+ "idle": {
+ "durationMs": 251.20000004768372,
+ "raf": {
+ "frames": 29,
+ "medianGapMs": 8.299999999999272,
+ "p95GapMs": 9.899999999999636,
+ "maxGapMs": 10.100000000000364,
+ "gapsOver33ms": 0,
+ "gapsOver50ms": 0,
+ "gapsMs": [
+ 8.199999999999818,
+ 8.400000000000546,
+ 7.699999999999818,
+ 9.099999999999454,
+ 8.100000000000364,
+ 8.5,
+ 8.300000000000182,
+ 7.699999999999818,
+ 8.100000000000364,
+ 9.199999999999818,
+ 6.899999999999636,
+ 8.5,
+ 9.800000000000182,
+ 8,
+ 7.100000000000364,
+ 9.899999999999636,
+ 7.100000000000364,
+ 9.599999999999454,
+ 8,
+ 8.400000000000546,
+ 6.899999999999636,
+ 10.100000000000364,
+ 7.599999999999454,
+ 8.800000000000182,
+ 8.199999999999818,
+ 8.400000000000546,
+ 8.299999999999272,
+ 7.800000000000182,
+ 9
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 0,
+ "RecalcStyleCount": 0,
+ "LayoutDuration": 0,
+ "RecalcStyleDuration": 0,
+ "DevToolsCommandDuration": 0.00008099999999999948,
+ "ScriptDuration": 0.00037300000000000527,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.006313000000000013,
+ "TaskOtherDuration": 0.005858999999999948
+ }
+ },
+ "interaction": {
+ "durationMs": 9364.799999952316,
+ "raf": {
+ "frames": 70,
+ "medianGapMs": 183.5,
+ "p95GapMs": 308.6999999999998,
+ "maxGapMs": 316.59999999999945,
+ "gapsOver33ms": 42,
+ "gapsOver50ms": 42,
+ "gapsMs": [
+ 8.300000000000182,
+ 8.299999999999272,
+ 8.400000000000546,
+ 316.59999999999945,
+ 8.400000000000546,
+ 316,
+ 9.099999999999454,
+ 316.40000000000055,
+ 308.39999999999964,
+ 308.6999999999998,
+ 308,
+ 300.10000000000036,
+ 308.3000000000002,
+ 299.89999999999964,
+ 308.40000000000055,
+ 183.39999999999964,
+ 183.10000000000036,
+ 183.5,
+ 181.89999999999964,
+ 184.70000000000073,
+ 191.6999999999989,
+ 183.60000000000036,
+ 191.39999999999964,
+ 183.3000000000011,
+ 191.6999999999989,
+ 191.70000000000073,
+ 191.79999999999927,
+ 191.39999999999964,
+ 199.10000000000036,
+ 192.60000000000036,
+ 191.79999999999927,
+ 200,
+ 191.10000000000036,
+ 191.70000000000073,
+ 200.5,
+ 190.29999999999927,
+ 209.60000000000036,
+ 207.89999999999964,
+ 192.20000000000073,
+ 200.39999999999964,
+ 199.89999999999964,
+ 190,
+ 193.10000000000036,
+ 158.60000000000036,
+ 191.29999999999927,
+ 150,
+ 150,
+ 8.399999999999636,
+ 8.300000000001091,
+ 8.299999999999272,
+ 8.300000000001091,
+ 8.399999999999636,
+ 8.299999999999272,
+ 8.399999999999636,
+ 8.300000000001091,
+ 8.399999999999636,
+ 8.200000000000728,
+ 8.299999999999272,
+ 7,
+ 9.700000000000728,
+ 8.399999999999636,
+ 8.299999999999272,
+ 8.300000000001091,
+ 8.399999999999636,
+ 8.299999999999272,
+ 8.300000000001091,
+ 8.299999999999272,
+ 8.399999999999636,
+ 8.399999999999636,
+ 8.300000000001091
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 1,
+ "RecalcStyleCount": 46,
+ "LayoutDuration": 0.00005699999999999976,
+ "RecalcStyleDuration": 0.011046,
+ "DevToolsCommandDuration": 0.005861999999999999,
+ "ScriptDuration": 0.07453200000000001,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.16966199999999998,
+ "TaskOtherDuration": 0.07816500000000004
+ }
+ },
+ "cdpBefore": {
+ "Timestamp": 1845506.264802,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5259,
+ "LayoutObjects": 5798,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 27651,
+ "Resources": 160,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 27,
+ "LayoutCount": 12,
+ "RecalcStyleCount": 21,
+ "LayoutDuration": 0.01034,
+ "RecalcStyleDuration": 0.037847,
+ "DevToolsCommandDuration": 0.014202,
+ "ScriptDuration": 0.06369,
+ "V8CompileDuration": 0.000057,
+ "TaskDuration": 0.819974,
+ "TaskOtherDuration": 0.693838,
+ "ThreadTime": 0.570848,
+ "ProcessTime": 2.719045,
+ "JSHeapUsedSize": 202127532,
+ "JSHeapTotalSize": 251920384,
+ "FirstMeaningfulPaint": 1845504.537744,
+ "DomContentLoaded": 1845501.969546,
+ "NavigationStart": 1845501.550727
+ },
+ "cdpAfter": {
+ "Timestamp": 1845515.62814,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5308,
+ "LayoutObjects": 4793,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 24739,
+ "Resources": 129,
+ "ContextLifecycleStateObservers": 40,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 82,
+ "LayoutCount": 13,
+ "RecalcStyleCount": 67,
+ "LayoutDuration": 0.010397,
+ "RecalcStyleDuration": 0.048893,
+ "DevToolsCommandDuration": 0.020064,
+ "ScriptDuration": 0.138222,
+ "V8CompileDuration": 0.000057,
+ "TaskDuration": 0.989636,
+ "TaskOtherDuration": 0.772003,
+ "ThreadTime": 0.726182,
+ "ProcessTime": 3.115699,
+ "JSHeapUsedSize": 198140424,
+ "JSHeapTotalSize": 259522560,
+ "FirstMeaningfulPaint": 1845504.537744,
+ "DomContentLoaded": 1845501.969546,
+ "NavigationStart": 1845501.550727
+ },
+ "errors": [],
+ "failed": []
+ },
+ {
+ "repeat": 3,
+ "beforePose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "afterPose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -109962411235333930000,
+ -40387395627789610000,
+ -116972221966195500000
+ ],
+ "orientationXyzw": [
+ -0.0937021412901332,
+ -0.3123404709671109,
+ -5.5050914713649234e-18,
+ 0.9453376851229868
+ ]
+ }
+ },
+ "distanceM": 8165459600736674000,
+ "distanceRadii": 6.096653162046668,
+ "projectedRadiusPixels": 204.55157392967894
+ },
+ "beforeDom": {
+ "totalElements": 18068,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 85,
+ "eagerImagesPending": 0
+ },
+ "afterDom": {
+ "totalElements": 18068,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 85,
+ "eagerImagesPending": 0
+ },
+ "inputDurationMs": 8111.016250000001,
+ "idle": {
+ "durationMs": 247.69999980926514,
+ "raf": {
+ "frames": 29,
+ "medianGapMs": 8.300000000000182,
+ "p95GapMs": 10.100000000000364,
+ "maxGapMs": 10.200000000000728,
+ "gapsOver33ms": 0,
+ "gapsOver50ms": 0,
+ "gapsMs": [
+ 8.300000000000182,
+ 10,
+ 7.899999999999636,
+ 8.399999999999636,
+ 8.400000000000546,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.300000000000182,
+ 6.800000000000182,
+ 9.099999999999454,
+ 8.400000000000546,
+ 7.599999999999454,
+ 8.199999999999818,
+ 10.200000000000728,
+ 7.899999999999636,
+ 8.5,
+ 6.899999999999636,
+ 10.100000000000364,
+ 6.899999999999636,
+ 9.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 8.400000000000546,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.300000000000182,
+ 7.399999999999636,
+ 8,
+ 9.900000000000546
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 0,
+ "RecalcStyleCount": 0,
+ "LayoutDuration": 0,
+ "RecalcStyleDuration": 0,
+ "DevToolsCommandDuration": 0.00006300000000000056,
+ "ScriptDuration": 0.00030000000000000165,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.0032200000000000006,
+ "TaskOtherDuration": 0.0028569999999999984
+ }
+ },
+ "interaction": {
+ "durationMs": 9398.600000143051,
+ "raf": {
+ "frames": 72,
+ "medianGapMs": 183.39999999999964,
+ "p95GapMs": 308.5,
+ "maxGapMs": 600,
+ "gapsOver33ms": 41,
+ "gapsOver50ms": 41,
+ "gapsMs": [
+ 8.400000000000546,
+ 7.399999999999636,
+ 7.600000000000364,
+ 26.599999999999454,
+ 600,
+ 8.300000000000182,
+ 308.5,
+ 8.199999999999818,
+ 308.40000000000055,
+ 307,
+ 309.59999999999945,
+ 308.40000000000055,
+ 308.3000000000002,
+ 308.2999999999993,
+ 300,
+ 191.70000000000073,
+ 191.6999999999989,
+ 200,
+ 183.3000000000011,
+ 183.39999999999964,
+ 16.600000000000364,
+ 366.6999999999989,
+ 58.30000000000109,
+ 8.399999999999636,
+ 191.60000000000036,
+ 191.5,
+ 91.79999999999927,
+ 191.8000000000011,
+ 191.59999999999854,
+ 191.8000000000011,
+ 191.5,
+ 191.6999999999989,
+ 190.60000000000036,
+ 201,
+ 191.70000000000073,
+ 190.79999999999927,
+ 199.89999999999964,
+ 191.3000000000011,
+ 201.39999999999964,
+ 190.29999999999927,
+ 201.3000000000011,
+ 191.60000000000036,
+ 200,
+ 200.09999999999854,
+ 200,
+ 190.40000000000146,
+ 159.59999999999854,
+ 191.60000000000036,
+ 149.39999999999964,
+ 9.100000000000364,
+ 8.200000000000728,
+ 8.299999999999272,
+ 8.399999999999636,
+ 8.300000000001091,
+ 8.299999999999272,
+ 8.399999999999636,
+ 8.400000000001455,
+ 6.599999999998545,
+ 10.400000000001455,
+ 7.799999999999272,
+ 8.399999999999636,
+ 8.300000000001091,
+ 8.399999999999636,
+ 8.399999999999636,
+ 8.299999999999272,
+ 8.400000000001455,
+ 6.699999999998909,
+ 10.200000000000728,
+ 8,
+ 8.399999999999636,
+ 8.299999999999272,
+ 8.300000000001091
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 1,
+ "RecalcStyleCount": 47,
+ "LayoutDuration": 0.000056000000000000494,
+ "RecalcStyleDuration": 0.010797000000000001,
+ "DevToolsCommandDuration": 0.005341,
+ "ScriptDuration": 0.07277800000000001,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.16791500000000004,
+ "TaskOtherDuration": 0.07894299999999999
+ }
+ },
+ "cdpBefore": {
+ "Timestamp": 1845520.555302,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5259,
+ "LayoutObjects": 5787,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 27651,
+ "Resources": 140,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 23,
+ "LayoutCount": 10,
+ "RecalcStyleCount": 23,
+ "LayoutDuration": 0.010364,
+ "RecalcStyleDuration": 0.036041,
+ "DevToolsCommandDuration": 0.013583,
+ "ScriptDuration": 0.060449,
+ "V8CompileDuration": 0.000055,
+ "TaskDuration": 0.811537,
+ "TaskOtherDuration": 0.691045,
+ "ThreadTime": 0.563435,
+ "ProcessTime": 2.764552,
+ "JSHeapUsedSize": 199092692,
+ "JSHeapTotalSize": 250347520,
+ "FirstMeaningfulPaint": 1845518.64072,
+ "DomContentLoaded": 1845516.124349,
+ "NavigationStart": 1845515.697699
+ },
+ "cdpAfter": {
+ "Timestamp": 1845529.952898,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5256,
+ "LayoutObjects": 4793,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 24739,
+ "Resources": 129,
+ "ContextLifecycleStateObservers": 40,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 57,
+ "LayoutCount": 11,
+ "RecalcStyleCount": 70,
+ "LayoutDuration": 0.01042,
+ "RecalcStyleDuration": 0.046838,
+ "DevToolsCommandDuration": 0.018924,
+ "ScriptDuration": 0.133227,
+ "V8CompileDuration": 0.000055,
+ "TaskDuration": 0.979452,
+ "TaskOtherDuration": 0.769988,
+ "ThreadTime": 0.716982,
+ "ProcessTime": 3.160583,
+ "JSHeapUsedSize": 193513052,
+ "JSHeapTotalSize": 258211840,
+ "FirstMeaningfulPaint": 1845518.64072,
+ "DomContentLoaded": 1845516.124349,
+ "NavigationStart": 1845515.697699
+ },
+ "errors": [],
+ "failed": []
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/performance-background-control.json b/src/objects/omega-centauri/evidence/2026-09-20/performance-background-control.json
new file mode 100644
index 0000000000..c4873c8363
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/performance-background-control.json
@@ -0,0 +1,1023 @@
+{
+ "claim": "One controlled page-only background visibility pair; not a shipped fix or desktop FPS",
+ "base": "http://127.0.0.1:4219",
+ "measuredAt": "2026-09-20T21:25:36.898Z",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "deviceScaleFactor": 1,
+ "browser": "148.0.7778.96",
+ "headless": true,
+ "hardware": {
+ "gpu": {
+ "devices": [
+ {
+ "vendorId": 65535,
+ "deviceId": 65535,
+ "vendorString": "Google Inc. (Google)",
+ "deviceString": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (LLVM 10.0.0) (0x0000C0DE)), SwiftShader driver-5.0.0)",
+ "driverVendor": "SwANGLE",
+ "driverVersion": "5.0.0"
+ }
+ ],
+ "auxAttributes": {
+ "amdSwitchable": false,
+ "canSupportThreadedTextureMailbox": false,
+ "directRenderingVersion": "",
+ "displayType": "ANGLE_SWIFTSHADER",
+ "glExtensions": "GL_AMD_performance_monitor GL_ANGLE_base_vertex_base_instance_shader_builtin GL_ANGLE_blob_cache GL_ANGLE_client_arrays GL_ANGLE_clip_cull_distance GL_ANGLE_compressed_texture_etc GL_ANGLE_copy_texture_3d GL_ANGLE_depth_texture GL_ANGLE_framebuffer_blit GL_ANGLE_framebuffer_multisample GL_ANGLE_get_image GL_ANGLE_get_serialized_context_string GL_ANGLE_get_tex_level_parameter GL_ANGLE_instanced_arrays GL_ANGLE_memory_object_flags GL_ANGLE_memory_size GL_ANGLE_multi_draw GL_ANGLE_pack_reverse_row_order GL_ANGLE_polygon_mode GL_ANGLE_program_binary_readiness_query GL_ANGLE_program_cache_control GL_ANGLE_read_only_depth_stencil_feedback_loops GL_ANGLE_relaxed_vertex_attribute_type GL_ANGLE_renderability_validation GL_ANGLE_request_extension GL_ANGLE_rgbx_internal_format GL_ANGLE_robust_client_memory GL_ANGLE_robust_fragment_shader_output GL_ANGLE_robust_resource_initialization GL_ANGLE_shader_pixel_local_storage GL_ANGLE_shader_pixel_local_storage_coherent GL_ANGLE_stencil_texturing GL_ANGLE_texture_compression_dxt3 GL_ANGLE_texture_compression_dxt5 GL_ANGLE_texture_multisample GL_ANGLE_texture_usage GL_ANGLE_translated_shader_source GL_ANGLE_vulkan_image GL_ANGLE_yuv_internal_format GL_APPLE_clip_distance GL_ARM_rgba8 GL_ARM_shader_framebuffer_fetch GL_ARM_shader_framebuffer_fetch_depth_stencil GL_CHROMIUM_bind_generates_resource GL_CHROMIUM_bind_uniform_location GL_CHROMIUM_copy_compressed_texture GL_CHROMIUM_copy_texture GL_CHROMIUM_lose_context GL_EXT_EGL_image_array GL_EXT_EGL_image_external_wrap_modes GL_EXT_EGL_image_storage GL_EXT_base_instance GL_EXT_blend_minmax GL_EXT_buffer_storage GL_EXT_clear_texture GL_EXT_clip_control GL_EXT_clip_cull_distance GL_EXT_color_buffer_float GL_EXT_color_buffer_half_float GL_EXT_compressed_ETC1_RGB8_sub_texture GL_EXT_conservative_depth GL_EXT_copy_image GL_EXT_debug_label GL_EXT_debug_marker GL_EXT_depth_clamp GL_EXT_discard_framebuffer GL_EXT_disjoint_timer_query GL_EXT_draw_buffers GL_EXT_draw_buffers_indexed GL_EXT_draw_elements_base_vertex GL_EXT_float_blend GL_EXT_frag_depth GL_EXT_instanced_arrays GL_EXT_map_buffer_range GL_EXT_memory_object GL_EXT_memory_object_fd GL_EXT_occlusion_query_boolean GL_EXT_polygon_offset_clamp GL_EXT_read_format_bgra GL_EXT_robustness GL_EXT_sRGB GL_EXT_sRGB_write_control GL_EXT_semaphore GL_EXT_separate_shader_objects GL_EXT_shader_framebuffer_fetch GL_EXT_shader_framebuffer_fetch_non_coherent GL_EXT_shader_non_constant_global_initializers GL_EXT_shader_texture_lod GL_EXT_shadow_samplers GL_EXT_texture_border_clamp GL_EXT_texture_compression_bptc GL_EXT_texture_compression_dxt1 GL_EXT_texture_compression_rgtc GL_EXT_texture_compression_s3tc_srgb GL_EXT_texture_cube_map_array GL_EXT_texture_filter_anisotropic GL_EXT_texture_format_BGRA8888 GL_EXT_texture_mirror_clamp_to_edge GL_EXT_texture_rg GL_EXT_texture_sRGB_R8 GL_EXT_texture_sRGB_RG8 GL_EXT_texture_sRGB_decode GL_EXT_texture_shadow_lod GL_EXT_texture_storage GL_EXT_texture_type_2_10_10_10_REV GL_EXT_unpack_subimage GL_KHR_blend_equation_advanced GL_KHR_debug GL_KHR_robust_buffer_access_behavior GL_KHR_robustness GL_KHR_texture_compression_astc_ldr GL_NV_depth_buffer_float2 GL_NV_fence GL_NV_framebuffer_blit GL_NV_pack_subimage GL_NV_pixel_buffer_object GL_NV_polygon_mode GL_NV_read_depth GL_NV_read_depth_stencil GL_NV_read_stencil GL_NV_shader_noperspective_interpolation GL_OES_EGL_image GL_OES_EGL_image_external GL_OES_EGL_image_external_essl3 GL_OES_EGL_sync GL_OES_compressed_EAC_R11_signed_texture GL_OES_compressed_EAC_R11_unsigned_texture GL_OES_compressed_EAC_RG11_signed_texture GL_OES_compressed_EAC_RG11_unsigned_texture GL_OES_compressed_ETC1_RGB8_texture GL_OES_compressed_ETC2_RGB8_texture GL_OES_compressed_ETC2_RGBA8_texture GL_OES_compressed_ETC2_punchthroughA_RGBA8_texture GL_OES_compressed_ETC2_punchthroughA_sRGB8_alpha_texture GL_OES_compressed_ETC2_sRGB8_alpha8_texture GL_OES_compressed_ETC2_sRGB8_texture GL_OES_compressed_paletted_texture GL_OES_copy_image GL_OES_depth24 GL_OES_depth32 GL_OES_depth_texture GL_OES_depth_texture_cube_map GL_OES_draw_buffers_indexed GL_OES_draw_elements_base_vertex GL_OES_element_index_uint GL_OES_fbo_render_mipmap GL_OES_get_program_binary GL_OES_mapbuffer GL_OES_packed_depth_stencil GL_OES_required_internalformat GL_OES_rgb8_rgba8 GL_OES_sample_shading GL_OES_sample_variables GL_OES_shader_multisample_interpolation GL_OES_standard_derivatives GL_OES_surfaceless_context GL_OES_texture_3D GL_OES_texture_border_clamp GL_OES_texture_cube_map_array GL_OES_texture_float GL_OES_texture_float_linear GL_OES_texture_half_float GL_OES_texture_half_float_linear GL_OES_texture_npot GL_OES_texture_stencil8 GL_OES_texture_storage_multisample_2d_array GL_OES_vertex_array_object GL_OES_vertex_half_float GL_OES_vertex_type_10_10_10_2 GL_OVR_multiview GL_OVR_multiview2 ",
+ "glImplementationParts": "(gl=egl-angle,angle=swiftshader)",
+ "glRenderer": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (LLVM 10.0.0) (0x0000C0DE)), SwiftShader driver-5.0.0)",
+ "glResetNotificationStrategy": 33362,
+ "glVendor": "Google Inc. (Google)",
+ "glVersion": "OpenGL ES 3.0 (ANGLE 2.1.27471 git hash: cc0e3572e878)",
+ "glWsExtensions": "",
+ "glWsVendor": "",
+ "glWsVersion": "",
+ "inProcessGpu": false,
+ "initializationTime": 0.025957,
+ "isAsan": false,
+ "isClangCoverage": false,
+ "jpegDecodeAcceleratorSupported": false,
+ "maxMsaaSamples": "4",
+ "optimus": false,
+ "passthroughCmdDecoder": true,
+ "pixelShaderVersion": "3.00",
+ "processCrashCount": 0,
+ "sandboxed": false,
+ "skiaBackendType": "GraphiteDawnVulkan",
+ "subpixelFontRendering": true,
+ "targetCpuBits": 64,
+ "vertexShaderVersion": "3.00",
+ "visibilityCallbackCallCount": 0
+ },
+ "featureStatus": {
+ "2d_canvas": "unavailable_software",
+ "direct_rendering_display_compositor": "disabled_off_ok",
+ "gpu_compositing": "disabled_software",
+ "multiple_raster_threads": "enabled_on",
+ "opengl": "disabled_off",
+ "rasterization": "disabled_software",
+ "raw_draw": "disabled_off_ok",
+ "skia_graphite": "disabled_off",
+ "trees_in_viz": "disabled_off",
+ "video_decode": "disabled_software",
+ "video_encode": "disabled_software",
+ "webgl": "unavailable_software",
+ "webgpu": "unavailable_software",
+ "webnn": "unavailable_software"
+ },
+ "driverBugWorkarounds": [],
+ "videoDecoding": [],
+ "videoEncoding": []
+ }
+ },
+ "node": "v22.23.2",
+ "platform": "darwin",
+ "arch": "arm64",
+ "protocol": {
+ "nativeUrl": "/sun/?focus=omega-centauri",
+ "warmup": "networkidle, then30 RAF",
+ "idleFrames": 30,
+ "drag": {
+ "from": [
+ 720,
+ 500
+ ],
+ "to": [
+ 920,
+ 560
+ ],
+ "steps": 40
+ },
+ "settleFrames": 30,
+ "noCameraOverride": true,
+ "noSourceChanges": true,
+ "hiddenSelector": "[data-volume-lens-object]:not([data-volume-lens-object=omega-centauri]),[data-volume-opacity]",
+ "scope": "Entire non-Omega volume model roots including their embedded catalogue star nodes; global ordinary stars, shell and markers retained; runtime publication JS unchanged"
+ },
+ "inputPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ },
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "bytes": 13194,
+ "sha256": "39b882f2d0ddf921b50827ba267fe8b83ba61dd6dc27c0ff469115a077045aea"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-universe-runtime.ts",
+ "bytes": 46748,
+ "sha256": "14f1953aa5ba1734b88532980cd2078044225c3b42d59b0cf67831c0dba1b3e0"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-world-context.ts",
+ "bytes": 75841,
+ "sha256": "00758c8d75054092c209c68b4e311605a4a886d59fda5b36cdf86bf5d41d9ab2"
+ },
+ {
+ "path": "src/renderers/css/dist/universe.js",
+ "bytes": 10185,
+ "sha256": "ce237fcd30c24ab63ed72016f0f8309de1fe0c2f0a6370649af54b1d8f3569a2"
+ }
+ ],
+ "reports": [
+ {
+ "repeat": 1,
+ "mode": "control",
+ "targetSelector": "[data-volume-lens-object]:not([data-volume-lens-object=\"omega-centauri\"]),[data-volume-opacity]",
+ "targetDisplays": [
+ {
+ "id": "milky-way-overview",
+ "display": "block"
+ },
+ {
+ "id": "lmc",
+ "display": "block"
+ },
+ {
+ "id": "m42",
+ "display": "none"
+ },
+ {
+ "id": "m8",
+ "display": "block"
+ },
+ {
+ "id": "smc",
+ "display": "block"
+ }
+ ],
+ "afterTargetDisplays": [
+ {
+ "id": "milky-way-overview",
+ "display": "block"
+ },
+ {
+ "id": "lmc",
+ "display": "block"
+ },
+ {
+ "id": "m42",
+ "display": "none"
+ },
+ {
+ "id": "m8",
+ "display": "block"
+ },
+ {
+ "id": "smc",
+ "display": "block"
+ }
+ ],
+ "beforePose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "afterPose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -109962411235333930000,
+ -40387395627789610000,
+ -116972221966195500000
+ ],
+ "orientationXyzw": [
+ -0.0937021412901332,
+ -0.3123404709671109,
+ -5.5050914713649234e-18,
+ 0.9453376851229868
+ ]
+ }
+ },
+ "distanceM": 8165459600736674000,
+ "distanceRadii": 6.096653162046668,
+ "projectedRadiusPixels": 204.55157392967894
+ },
+ "beforeDom": {
+ "totalElements": 29063,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0810833,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "block",
+ "opacity": 0.566307,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 79,
+ "eagerImagesPending": 0
+ },
+ "afterDom": {
+ "totalElements": 29063,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0739867,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "block",
+ "opacity": 0.576726,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 79,
+ "eagerImagesPending": 0
+ },
+ "inputDurationMs": 33056.020625000005,
+ "idle": {
+ "durationMs": 248.80000019073486,
+ "raf": {
+ "frames": 30,
+ "medianGapMs": 8.300000000000182,
+ "p95GapMs": 8.599999999999454,
+ "maxGapMs": 8.700000000000728,
+ "gapsOver33ms": 0,
+ "gapsOver50ms": 0,
+ "gapsMs": [
+ 8.334000000000742,
+ 8.299999999999272,
+ 8.400000000000546,
+ 8.300000000000182,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.199999999999818,
+ 8.5,
+ 8.400000000000546,
+ 8.199999999999818,
+ 8.399999999999636,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.400000000000546,
+ 7.599999999999454,
+ 8.700000000000728,
+ 8.599999999999454,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.400000000000546,
+ 8.300000000000182,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.400000000000546
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 0,
+ "RecalcStyleCount": 0,
+ "LayoutDuration": 0,
+ "RecalcStyleDuration": 0,
+ "DevToolsCommandDuration": 0.00007100000000000162,
+ "ScriptDuration": 0.0003110000000000057,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.005376000000000047,
+ "TaskOtherDuration": 0.004994000000000054
+ }
+ },
+ "interaction": {
+ "durationMs": 34942.700000047684,
+ "raf": {
+ "frames": 74,
+ "medianGapMs": 449.1000000000058,
+ "p95GapMs": 1283.300000000003,
+ "maxGapMs": 2308.2999999999993,
+ "gapsOver33ms": 41,
+ "gapsOver50ms": 41,
+ "gapsMs": [
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 25,
+ 933.3000000000011,
+ 8.399999999999636,
+ 8.299999999999272,
+ 958.3999999999996,
+ 941.6000000000004,
+ 940.8000000000011,
+ 625.8999999999996,
+ 1025,
+ 1008.1000000000004,
+ 1000.1999999999989,
+ 841.6000000000004,
+ 841.7999999999993,
+ 866.2000000000007,
+ 733.7000000000007,
+ 1458.3999999999978,
+ 16.700000000000728,
+ 2308.2999999999993,
+ 1141.7000000000007,
+ 74.90000000000146,
+ 8.399999999997817,
+ 1108.300000000003,
+ 1116.699999999997,
+ 1283.300000000003,
+ 1066.699999999997,
+ 1058.300000000003,
+ 16.69999999999709,
+ 2100,
+ 1050,
+ 141.60000000000218,
+ 1040.9999999999964,
+ 1034,
+ 1033.300000000003,
+ 1025,
+ 1000,
+ 466.6999999999971,
+ 449.1000000000058,
+ 450.1999999999971,
+ 450.6999999999971,
+ 458.3000000000029,
+ 450,
+ 457.59999999999854,
+ 450.8000000000029,
+ 458.29999999999563,
+ 166.70000000000437,
+ 450,
+ 7.900000000001455,
+ 158.09999999999854,
+ 9,
+ 8,
+ 8.69999999999709,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.400000000001455,
+ 8.299999999995634,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 7.80000000000291,
+ 8.799999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.30000000000291,
+ 8.400000000001455,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 5,
+ "RecalcStyleCount": 49,
+ "LayoutDuration": 0.0009560000000000003,
+ "RecalcStyleDuration": 0.08825000000000001,
+ "DevToolsCommandDuration": 0.007258999999999998,
+ "ScriptDuration": 0.124651,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.5020550000000001,
+ "TaskOtherDuration": 0.28093899999999994
+ }
+ },
+ "cdpBefore": {
+ "Timestamp": 1844624.518927,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5259,
+ "LayoutObjects": 10202,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 38657,
+ "Resources": 938,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 499,
+ "LayoutCount": 11,
+ "RecalcStyleCount": 24,
+ "LayoutDuration": 0.013167,
+ "RecalcStyleDuration": 0.055495,
+ "DevToolsCommandDuration": 0.026869,
+ "ScriptDuration": 0.07459,
+ "V8CompileDuration": 0.000056,
+ "TaskDuration": 1.107298,
+ "TaskOtherDuration": 0.937121,
+ "ThreadTime": 0.855672,
+ "ProcessTime": 3.490485,
+ "JSHeapUsedSize": 217481452,
+ "JSHeapTotalSize": 269746176,
+ "FirstMeaningfulPaint": 1844619.559197,
+ "DomContentLoaded": 1844616.945784,
+ "NavigationStart": 1844616.534674
+ },
+ "cdpAfter": {
+ "Timestamp": 1844659.460535,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5316,
+ "LayoutObjects": 9034,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 35745,
+ "Resources": 481,
+ "ContextLifecycleStateObservers": 40,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 3,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 43,
+ "LayoutCount": 16,
+ "RecalcStyleCount": 73,
+ "LayoutDuration": 0.014123,
+ "RecalcStyleDuration": 0.143745,
+ "DevToolsCommandDuration": 0.034128,
+ "ScriptDuration": 0.199241,
+ "V8CompileDuration": 0.000056,
+ "TaskDuration": 1.609353,
+ "TaskOtherDuration": 1.21806,
+ "ThreadTime": 1.312425,
+ "ProcessTime": 5.117526,
+ "JSHeapUsedSize": 203928852,
+ "JSHeapTotalSize": 208666624,
+ "FirstMeaningfulPaint": 1844619.559197,
+ "DomContentLoaded": 1844616.945784,
+ "NavigationStart": 1844616.534674
+ },
+ "errors": [],
+ "failed": []
+ },
+ {
+ "repeat": 2,
+ "mode": "background-hidden",
+ "targetSelector": "[data-volume-lens-object]:not([data-volume-lens-object=\"omega-centauri\"]),[data-volume-opacity]",
+ "targetDisplays": [
+ {
+ "id": "milky-way-overview",
+ "display": "none"
+ },
+ {
+ "id": "lmc",
+ "display": "none"
+ },
+ {
+ "id": "m8",
+ "display": "none"
+ },
+ {
+ "id": "m42",
+ "display": "none"
+ },
+ {
+ "id": "smc",
+ "display": "none"
+ }
+ ],
+ "afterTargetDisplays": [
+ {
+ "id": "milky-way-overview",
+ "display": "none"
+ },
+ {
+ "id": "lmc",
+ "display": "none"
+ },
+ {
+ "id": "m8",
+ "display": "none"
+ },
+ {
+ "id": "m42",
+ "display": "none"
+ },
+ {
+ "id": "smc",
+ "display": "none"
+ }
+ ],
+ "beforePose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "afterPose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -109962411235333930000,
+ -40387395627789610000,
+ -116972221966195500000
+ ],
+ "orientationXyzw": [
+ -0.0937021412901332,
+ -0.3123404709671109,
+ -5.5050914713649234e-18,
+ 0.9453376851229868
+ ]
+ }
+ },
+ "distanceM": 8165459600736674000,
+ "distanceRadii": 6.096653162046668,
+ "projectedRadiusPixels": 204.55157392967894
+ },
+ "beforeDom": {
+ "totalElements": 29064,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "lmc",
+ "display": "none",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "m8",
+ "display": "none",
+ "opacity": 0.0810833,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "smc",
+ "display": "none",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0.566307,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 78,
+ "eagerImagesPending": 0
+ },
+ "afterDom": {
+ "totalElements": 29064,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "lmc",
+ "display": "none",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "m8",
+ "display": "none",
+ "opacity": 0.0739867,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "smc",
+ "display": "none",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0.576726,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 78,
+ "eagerImagesPending": 0
+ },
+ "inputDurationMs": 8381.190499999997,
+ "idle": {
+ "durationMs": 246.39999985694885,
+ "raf": {
+ "frames": 30,
+ "medianGapMs": 8.300000000000182,
+ "p95GapMs": 9,
+ "maxGapMs": 9.100000000000364,
+ "gapsOver33ms": 0,
+ "gapsOver50ms": 0,
+ "gapsMs": [
+ 8.199999999999818,
+ 7.699999999999818,
+ 9.100000000000364,
+ 8.199999999999818,
+ 8.399999999999636,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 7.600000000000364,
+ 8.899999999999636,
+ 8.5,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.099999999999454,
+ 8.5,
+ 8.400000000000546,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.300000000000182,
+ 7.800000000000182,
+ 9,
+ 8.300000000000182,
+ 8.299999999999272,
+ 8.400000000000546,
+ 8.300000000000182,
+ 8.299999999999272,
+ 8.400000000000546
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 0,
+ "RecalcStyleCount": 0,
+ "LayoutDuration": 0,
+ "RecalcStyleDuration": 0,
+ "DevToolsCommandDuration": 0.00006899999999999962,
+ "ScriptDuration": 0.00022800000000000598,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.003882999999999859,
+ "TaskOtherDuration": 0.003585999999999978
+ }
+ },
+ "interaction": {
+ "durationMs": 9482.400000095367,
+ "raf": {
+ "frames": 69,
+ "medianGapMs": 191.59999999999854,
+ "p95GapMs": 308.40000000000055,
+ "maxGapMs": 325,
+ "gapsOver33ms": 42,
+ "gapsOver50ms": 42,
+ "gapsMs": [
+ 8.400000000000546,
+ 8.199999999999818,
+ 25.100000000000364,
+ 166.59999999999945,
+ 308.40000000000055,
+ 308.2999999999993,
+ 308.3000000000011,
+ 325,
+ 308.39999999999964,
+ 316.60000000000036,
+ 308.2999999999993,
+ 316.7000000000007,
+ 308.39999999999964,
+ 308.2999999999993,
+ 183.3000000000011,
+ 191.59999999999854,
+ 182.8000000000011,
+ 192.29999999999927,
+ 183.39999999999964,
+ 191.60000000000036,
+ 191.70000000000073,
+ 191.6999999999989,
+ 191.60000000000036,
+ 191.70000000000073,
+ 191.60000000000036,
+ 199.79999999999927,
+ 200.20000000000073,
+ 191.6999999999989,
+ 200,
+ 191.70000000000073,
+ 200,
+ 200,
+ 191.6999999999989,
+ 200,
+ 200,
+ 199.60000000000036,
+ 200.39999999999964,
+ 199.90000000000146,
+ 191.6999999999989,
+ 200,
+ 200,
+ 200,
+ 158.3000000000011,
+ 191.29999999999927,
+ 150.40000000000146,
+ 8.299999999999272,
+ 8.299999999999272,
+ 8.400000000001455,
+ 8.299999999999272,
+ 8.299999999999272,
+ 8.400000000001455,
+ 8.399999999997817,
+ 8.299999999999272,
+ 8.30000000000291,
+ 8.299999999999272,
+ 8.099999999998545,
+ 8.700000000000728,
+ 8.200000000000728,
+ 8.299999999999272,
+ 8.400000000001455,
+ 7.599999999998545,
+ 9.200000000000728,
+ 8.200000000000728,
+ 8.399999999997817,
+ 8.299999999999272,
+ 8.30000000000291,
+ 8.299999999999272,
+ 8.399999999997817,
+ 8.30000000000291
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 1,
+ "RecalcStyleCount": 44,
+ "LayoutDuration": 0.00005800000000000076,
+ "RecalcStyleDuration": 0.012986000000000004,
+ "DevToolsCommandDuration": 0.006305000000000002,
+ "ScriptDuration": 0.11778100000000001,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.228005,
+ "TaskOtherDuration": 0.09087500000000004
+ }
+ },
+ "cdpBefore": {
+ "Timestamp": 1844667.054258,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5261,
+ "LayoutObjects": 10213,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 38660,
+ "Resources": 966,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 504,
+ "LayoutCount": 14,
+ "RecalcStyleCount": 24,
+ "LayoutDuration": 0.012497,
+ "RecalcStyleDuration": 0.060965,
+ "DevToolsCommandDuration": 0.024578,
+ "ScriptDuration": 0.069118,
+ "V8CompileDuration": 0.000059,
+ "TaskDuration": 1.052571,
+ "TaskOtherDuration": 0.885354,
+ "ThreadTime": 0.817913,
+ "ProcessTime": 3.325807,
+ "JSHeapUsedSize": 218982068,
+ "JSHeapTotalSize": 268959744,
+ "FirstMeaningfulPaint": 1844662.444582,
+ "DomContentLoaded": 1844659.934278,
+ "NavigationStart": 1844659.537014
+ },
+ "cdpAfter": {
+ "Timestamp": 1844676.535466,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5374,
+ "LayoutObjects": 5214,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 35748,
+ "Resources": 468,
+ "ContextLifecycleStateObservers": 40,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 121,
+ "LayoutCount": 15,
+ "RecalcStyleCount": 68,
+ "LayoutDuration": 0.012555,
+ "RecalcStyleDuration": 0.073951,
+ "DevToolsCommandDuration": 0.030883,
+ "ScriptDuration": 0.186899,
+ "V8CompileDuration": 0.000059,
+ "TaskDuration": 1.280576,
+ "TaskOtherDuration": 0.976229,
+ "ThreadTime": 1.029277,
+ "ProcessTime": 3.814634,
+ "JSHeapUsedSize": 227496440,
+ "JSHeapTotalSize": 273940480,
+ "FirstMeaningfulPaint": 1844662.444582,
+ "DomContentLoaded": 1844659.934278,
+ "NavigationStart": 1844659.537014
+ },
+ "errors": [],
+ "failed": []
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/performance-baseline.json b/src/objects/omega-centauri/evidence/2026-09-20/performance-baseline.json
new file mode 100644
index 0000000000..4e657e2af9
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/performance-baseline.json
@@ -0,0 +1,1259 @@
+{
+ "claim": "Native-focus numerical performance baseline, not user FPS",
+ "base": "http://127.0.0.1:4219",
+ "measuredAt": "2026-09-20T21:21:37.945Z",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "deviceScaleFactor": 1,
+ "browser": "148.0.7778.96",
+ "headless": true,
+ "node": "v22.23.2",
+ "platform": "darwin",
+ "arch": "arm64",
+ "protocol": {
+ "nativeUrl": "/sun/?focus=omega-centauri",
+ "warmup": "networkidle, then30 RAF",
+ "idleFrames": 30,
+ "drag": {
+ "from": [
+ 720,
+ 500
+ ],
+ "to": [
+ 920,
+ 560
+ ],
+ "steps": 40
+ },
+ "settleFrames": 30,
+ "noCameraOverride": true,
+ "noSourceChanges": true
+ },
+ "inputPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ },
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "bytes": 13194,
+ "sha256": "39b882f2d0ddf921b50827ba267fe8b83ba61dd6dc27c0ff469115a077045aea"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-universe-runtime.ts",
+ "bytes": 46748,
+ "sha256": "14f1953aa5ba1734b88532980cd2078044225c3b42d59b0cf67831c0dba1b3e0"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-world-context.ts",
+ "bytes": 75841,
+ "sha256": "00758c8d75054092c209c68b4e311605a4a886d59fda5b36cdf86bf5d41d9ab2"
+ },
+ {
+ "path": "src/renderers/css/dist/universe.js",
+ "bytes": 10185,
+ "sha256": "ce237fcd30c24ab63ed72016f0f8309de1fe0c2f0a6370649af54b1d8f3569a2"
+ }
+ ],
+ "reports": [
+ {
+ "repeat": 1,
+ "beforePose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "afterPose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -109962411235333930000,
+ -40387395627789610000,
+ -116972221966195500000
+ ],
+ "orientationXyzw": [
+ -0.0937021412901332,
+ -0.3123404709671109,
+ -5.5050914713649234e-18,
+ 0.9453376851229868
+ ]
+ }
+ },
+ "distanceM": 8165459600736674000,
+ "distanceRadii": 6.096653162046668,
+ "projectedRadiusPixels": 204.55157392967894
+ },
+ "beforeDom": {
+ "totalElements": 29063,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0810833,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "block",
+ "opacity": 0.566307,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 79,
+ "eagerImagesPending": 0
+ },
+ "afterDom": {
+ "totalElements": 29063,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0739867,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "block",
+ "opacity": 0.576726,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 79,
+ "eagerImagesPending": 0
+ },
+ "inputDurationMs": 32255.737458,
+ "idle": {
+ "durationMs": 247.40000009536743,
+ "raf": {
+ "frames": 29,
+ "medianGapMs": 8.300000000000182,
+ "p95GapMs": 9,
+ "maxGapMs": 9.199999999999818,
+ "gapsOver33ms": 0,
+ "gapsOver50ms": 0,
+ "gapsMs": [
+ 9.199999999999818,
+ 8.199999999999818,
+ 8.400000000000546,
+ 7.800000000000182,
+ 8.899999999999636,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.299999999999272,
+ 8.400000000000546,
+ 8.300000000000182,
+ 7.899999999999636,
+ 8.899999999999636,
+ 7.700000000000728,
+ 8.899999999999636,
+ 8.199999999999818,
+ 8.5,
+ 8.300000000000182,
+ 8.100000000000364,
+ 8.5,
+ 8.399999999999636,
+ 8.100000000000364,
+ 8.5,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.399999999999636,
+ 7.699999999999818,
+ 9,
+ 8.199999999999818
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 0,
+ "RecalcStyleCount": 0,
+ "LayoutDuration": 0,
+ "RecalcStyleDuration": 0,
+ "DevToolsCommandDuration": 0.0001439999999999983,
+ "ScriptDuration": 0.0009179999999999883,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.02055399999999996,
+ "TaskOtherDuration": 0.019491999999999954
+ }
+ },
+ "interaction": {
+ "durationMs": 34616.39999985695,
+ "raf": {
+ "frames": 74,
+ "medianGapMs": 458.29999999999563,
+ "p95GapMs": 1150,
+ "maxGapMs": 2225,
+ "gapsOver33ms": 41,
+ "gapsOver50ms": 41,
+ "gapsMs": [
+ 9.300000000000182,
+ 8.199999999999818,
+ 8.300000000000182,
+ 8.399999999999636,
+ 16.699999999999818,
+ 8.199999999999818,
+ 16.800000000000182,
+ 33.19999999999982,
+ 916.7000000000007,
+ 16.69999999999891,
+ 940.7000000000007,
+ 942.6000000000004,
+ 958.3999999999996,
+ 1016.7000000000007,
+ 983.1999999999989,
+ 991.7000000000007,
+ 841.6000000000004,
+ 841.6999999999989,
+ 874.6000000000004,
+ 750.5,
+ 725,
+ 1158.2999999999993,
+ 1150,
+ 1158.2999999999993,
+ 2225,
+ 1116.6000000000022,
+ 1075,
+ 1083.5,
+ 1074.8999999999978,
+ 1075,
+ 1066.300000000003,
+ 1067.0999999999985,
+ 1041.5999999999985,
+ 8.30000000000291,
+ 1041.699999999997,
+ 1033.4000000000015,
+ 1032.5,
+ 166.59999999999854,
+ 1009.2000000000044,
+ 466.6999999999971,
+ 458.3000000000029,
+ 449.8999999999942,
+ 458.40000000000146,
+ 466.70000000000437,
+ 458.29999999999563,
+ 458.3000000000029,
+ 466.6999999999971,
+ 458.3000000000029,
+ 166.6999999999971,
+ 458.3000000000029,
+ 158.40000000000146,
+ 8.39999999999418,
+ 8.30000000000291,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.30000000000291,
+ 8.400000000001455,
+ 8.299999999995634,
+ 8.30000000000291,
+ 8.400000000001455,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.39999999999418,
+ 8.200000000004366,
+ 8.400000000001455,
+ 8.299999999995634,
+ 8.30000000000291
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 5,
+ "RecalcStyleCount": 47,
+ "LayoutDuration": 0.0010539999999999994,
+ "RecalcStyleDuration": 0.094471,
+ "DevToolsCommandDuration": 0.0072510000000000005,
+ "ScriptDuration": 0.13705799999999999,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.532591,
+ "TaskOtherDuration": 0.29275699999999993
+ }
+ },
+ "cdpBefore": {
+ "Timestamp": 1844319.413174,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5259,
+ "LayoutObjects": 10202,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 38646,
+ "Resources": 938,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 499,
+ "LayoutCount": 13,
+ "RecalcStyleCount": 22,
+ "LayoutDuration": 0.013495,
+ "RecalcStyleDuration": 0.056598,
+ "DevToolsCommandDuration": 0.017051,
+ "ScriptDuration": 0.078296,
+ "V8CompileDuration": 0.000058,
+ "TaskDuration": 1.138569,
+ "TaskOtherDuration": 0.973071,
+ "ThreadTime": 0.876668,
+ "ProcessTime": 3.652211,
+ "JSHeapUsedSize": 214989588,
+ "JSHeapTotalSize": 269746176,
+ "FirstMeaningfulPaint": 1844314.985271,
+ "DomContentLoaded": 1844312.233133,
+ "NavigationStart": 1844311.773126
+ },
+ "cdpAfter": {
+ "Timestamp": 1844354.027361,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5308,
+ "LayoutObjects": 9034,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 35734,
+ "Resources": 481,
+ "ContextLifecycleStateObservers": 40,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 3,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 40,
+ "LayoutCount": 18,
+ "RecalcStyleCount": 69,
+ "LayoutDuration": 0.014549,
+ "RecalcStyleDuration": 0.151069,
+ "DevToolsCommandDuration": 0.024302,
+ "ScriptDuration": 0.215354,
+ "V8CompileDuration": 0.000058,
+ "TaskDuration": 1.67116,
+ "TaskOtherDuration": 1.265828,
+ "ThreadTime": 1.365116,
+ "ProcessTime": 5.305769,
+ "JSHeapUsedSize": 203181924,
+ "JSHeapTotalSize": 208928768,
+ "FirstMeaningfulPaint": 1844314.985271,
+ "DomContentLoaded": 1844312.233133,
+ "NavigationStart": 1844311.773126
+ },
+ "errors": [],
+ "failed": []
+ },
+ {
+ "repeat": 2,
+ "beforePose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "afterPose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -109962411235333930000,
+ -40387395627789610000,
+ -116972221966195500000
+ ],
+ "orientationXyzw": [
+ -0.0937021412901332,
+ -0.3123404709671109,
+ -5.5050914713649234e-18,
+ 0.9453376851229868
+ ]
+ }
+ },
+ "distanceM": 8165459600736674000,
+ "distanceRadii": 6.096653162046668,
+ "projectedRadiusPixels": 204.55157392967894
+ },
+ "beforeDom": {
+ "totalElements": 29063,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0810833,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "block",
+ "opacity": 0.566307,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 84,
+ "eagerImagesPending": 0
+ },
+ "afterDom": {
+ "totalElements": 29063,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0739867,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "block",
+ "opacity": 0.576726,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 84,
+ "eagerImagesPending": 0
+ },
+ "inputDurationMs": 32173.033125,
+ "idle": {
+ "durationMs": 246.70000004768372,
+ "raf": {
+ "frames": 29,
+ "medianGapMs": 8.300000000000182,
+ "p95GapMs": 9,
+ "maxGapMs": 9.300000000000182,
+ "gapsOver33ms": 0,
+ "gapsOver50ms": 0,
+ "gapsMs": [
+ 8.599999999999454,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.400000000000546,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.399999999999636,
+ 8.300000000000182,
+ 8.300000000000182,
+ 7.899999999999636,
+ 8.800000000000182,
+ 7.5,
+ 9.300000000000182,
+ 8.199999999999818,
+ 8.300000000000182,
+ 7.699999999999818,
+ 9,
+ 7.699999999999818,
+ 8.400000000000546,
+ 9,
+ 8.199999999999818,
+ 8,
+ 8.699999999999818,
+ 8.300000000000182,
+ 8.300000000000182,
+ 8.399999999999636
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 0,
+ "RecalcStyleCount": 0,
+ "LayoutDuration": 0,
+ "RecalcStyleDuration": 0,
+ "DevToolsCommandDuration": 0.00012900000000000064,
+ "ScriptDuration": 0.001025000000000012,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.02099899999999999,
+ "TaskOtherDuration": 0.019845
+ }
+ },
+ "interaction": {
+ "durationMs": 34532.300000190735,
+ "raf": {
+ "frames": 71,
+ "medianGapMs": 458.29999999999563,
+ "p95GapMs": 1133.4000000000015,
+ "maxGapMs": 1158.4000000000015,
+ "gapsOver33ms": 41,
+ "gapsOver50ms": 41,
+ "gapsMs": [
+ 8.300000000000182,
+ 8.300000000000182,
+ 16.699999999999818,
+ 8.400000000000546,
+ 16.599999999999454,
+ 16.699999999999818,
+ 933.3000000000002,
+ 15.800000000000182,
+ 925.7999999999993,
+ 941.5,
+ 950.2000000000007,
+ 1025,
+ 975,
+ 1000,
+ 850,
+ 841.7000000000007,
+ 875,
+ 758.2999999999993,
+ 775,
+ 1150,
+ 1158.4000000000015,
+ 1149.8999999999978,
+ 1133.4000000000015,
+ 1108.2999999999993,
+ 1125,
+ 1083.4000000000015,
+ 1075,
+ 1075,
+ 1083.2999999999993,
+ 1058.2999999999993,
+ 1050.0999999999985,
+ 1066.6000000000022,
+ 1041.6000000000022,
+ 1041.699999999997,
+ 1041.699999999997,
+ 1016.6000000000058,
+ 466.6999999999971,
+ 458.3000000000029,
+ 458.3999999999942,
+ 466.6000000000058,
+ 458.29999999999563,
+ 458.5,
+ 458.20000000000437,
+ 458.5,
+ 457.59999999999854,
+ 175.59999999999854,
+ 458.1999999999971,
+ 157.90000000000146,
+ 9,
+ 8.200000000004366,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.400000000001455,
+ 8.299999999995634,
+ 8.30000000000291,
+ 8.30000000000291,
+ 8.39999999999418,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.30000000000291,
+ 8.400000000001455,
+ 8.299999999995634,
+ 8.30000000000291,
+ 8.30000000000291,
+ 8.39999999999418,
+ 8.30000000000291,
+ 8.400000000001455
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 5,
+ "RecalcStyleCount": 46,
+ "LayoutDuration": 0.0010360000000000005,
+ "RecalcStyleDuration": 0.093308,
+ "DevToolsCommandDuration": 0.006971999999999999,
+ "ScriptDuration": 0.13447599999999998,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.514086,
+ "TaskOtherDuration": 0.27829399999999993
+ }
+ },
+ "cdpBefore": {
+ "Timestamp": 1844361.1967,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5259,
+ "LayoutObjects": 10213,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 38646,
+ "Resources": 941,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 504,
+ "LayoutCount": 12,
+ "RecalcStyleCount": 21,
+ "LayoutDuration": 0.012336,
+ "RecalcStyleDuration": 0.053859,
+ "DevToolsCommandDuration": 0.016086,
+ "ScriptDuration": 0.074418,
+ "V8CompileDuration": 0.000054,
+ "TaskDuration": 1.088446,
+ "TaskOtherDuration": 0.931693,
+ "ThreadTime": 0.829258,
+ "ProcessTime": 3.432394,
+ "JSHeapUsedSize": 214960736,
+ "JSHeapTotalSize": 274202624,
+ "FirstMeaningfulPaint": 1844357.082668,
+ "DomContentLoaded": 1844354.546646,
+ "NavigationStart": 1844354.10523
+ },
+ "cdpAfter": {
+ "Timestamp": 1844395.727329,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5296,
+ "LayoutObjects": 9034,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 35734,
+ "Resources": 481,
+ "ContextLifecycleStateObservers": 40,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 3,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 36,
+ "LayoutCount": 17,
+ "RecalcStyleCount": 67,
+ "LayoutDuration": 0.013372,
+ "RecalcStyleDuration": 0.147167,
+ "DevToolsCommandDuration": 0.023058,
+ "ScriptDuration": 0.208894,
+ "V8CompileDuration": 0.000054,
+ "TaskDuration": 1.602532,
+ "TaskOtherDuration": 1.209987,
+ "ThreadTime": 1.30703,
+ "ProcessTime": 5.081607,
+ "JSHeapUsedSize": 202608160,
+ "JSHeapTotalSize": 208404480,
+ "FirstMeaningfulPaint": 1844357.082668,
+ "DomContentLoaded": 1844354.546646,
+ "NavigationStart": 1844354.10523
+ },
+ "errors": [],
+ "failed": []
+ },
+ {
+ "repeat": 3,
+ "beforePose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "afterPose": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -109962411235333930000,
+ -40387395627789610000,
+ -116972221966195500000
+ ],
+ "orientationXyzw": [
+ -0.0937021412901332,
+ -0.3123404709671109,
+ -5.5050914713649234e-18,
+ 0.9453376851229868
+ ]
+ }
+ },
+ "distanceM": 8165459600736674000,
+ "distanceRadii": 6.096653162046668,
+ "projectedRadiusPixels": 204.55157392967894
+ },
+ "beforeDom": {
+ "totalElements": 29063,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0810833,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "block",
+ "opacity": 0.566307,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 83,
+ "eagerImagesPending": 0
+ },
+ "afterDom": {
+ "totalElements": 29063,
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0,
+ "elements": 4068,
+ "planes": 2523,
+ "stars": 1500
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1784,
+ "planes": 726,
+ "stars": 1042
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0739867,
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 1,
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "block",
+ "opacity": 0.576726,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "imagesPending": 83,
+ "eagerImagesPending": 0
+ },
+ "inputDurationMs": 32703.860291000005,
+ "idle": {
+ "durationMs": 248.69999980926514,
+ "raf": {
+ "frames": 29,
+ "medianGapMs": 8.300000000000182,
+ "p95GapMs": 9.099999999999454,
+ "maxGapMs": 9.199999999999818,
+ "gapsOver33ms": 0,
+ "gapsOver50ms": 0,
+ "gapsMs": [
+ 8.300000000000182,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 8.300000000000182,
+ 7.600000000000364,
+ 9,
+ 8.099999999999454,
+ 8.700000000000728,
+ 7.5,
+ 9.199999999999818,
+ 8.199999999999818,
+ 8.399999999999636,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.100000000000364,
+ 8,
+ 9,
+ 8.199999999999818,
+ 8.399999999999636,
+ 7.700000000000728,
+ 9.099999999999454,
+ 8.199999999999818
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 0,
+ "RecalcStyleCount": 0,
+ "LayoutDuration": 0,
+ "RecalcStyleDuration": 0,
+ "DevToolsCommandDuration": 0.00011399999999999952,
+ "ScriptDuration": 0.0003989999999999966,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.00971500000000014,
+ "TaskOtherDuration": 0.009201999999999932
+ }
+ },
+ "interaction": {
+ "durationMs": 34633.5,
+ "raf": {
+ "frames": 71,
+ "medianGapMs": 458.3000000000029,
+ "p95GapMs": 1141.6000000000022,
+ "maxGapMs": 1150.0999999999985,
+ "gapsOver33ms": 40,
+ "gapsOver50ms": 40,
+ "gapsMs": [
+ 8.299999999999272,
+ 8.300000000000182,
+ 8.400000000000546,
+ 8.299999999999272,
+ 16.700000000000728,
+ 25,
+ 941.5999999999995,
+ 16.699999999999818,
+ 950,
+ 966.8000000000011,
+ 974.7999999999993,
+ 1058.3999999999996,
+ 1025,
+ 1016.7000000000007,
+ 825,
+ 833.2999999999993,
+ 841.6999999999989,
+ 725,
+ 724.9000000000015,
+ 1150.0999999999985,
+ 1141.7000000000007,
+ 1141.6000000000022,
+ 1141.699999999997,
+ 1116.7000000000007,
+ 1108.2999999999993,
+ 1083.300000000003,
+ 1075,
+ 1066.699999999997,
+ 1083.300000000003,
+ 1066.699999999997,
+ 1057.7000000000007,
+ 1075.6000000000022,
+ 1058.4000000000015,
+ 1050,
+ 1049.8999999999942,
+ 1016.6000000000058,
+ 482.79999999999563,
+ 909,
+ 457.70000000000437,
+ 467.29999999999563,
+ 458.3000000000029,
+ 466.59999999999854,
+ 458.40000000000146,
+ 458.29999999999563,
+ 175,
+ 466.70000000000437,
+ 166.59999999999854,
+ 8.400000000001455,
+ 7.599999999998545,
+ 9.099999999998545,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.400000000001455,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.299999999995634,
+ 8.400000000001455,
+ 8.30000000000291,
+ 8.400000000001455,
+ 8.299999999995634
+ ]
+ },
+ "longTasks": [],
+ "longTaskTotalMs": 0,
+ "cdpDelta": {
+ "LayoutCount": 5,
+ "RecalcStyleCount": 46,
+ "LayoutDuration": 0.001059000000000001,
+ "RecalcStyleDuration": 0.093461,
+ "DevToolsCommandDuration": 0.006645999999999999,
+ "ScriptDuration": 0.127967,
+ "V8CompileDuration": 0,
+ "TaskDuration": 0.4928450000000002,
+ "TaskOtherDuration": 0.26371200000000006
+ }
+ },
+ "cdpBefore": {
+ "Timestamp": 1844402.953677,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5259,
+ "LayoutObjects": 10213,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 38646,
+ "Resources": 941,
+ "ContextLifecycleStateObservers": 41,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 4,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 503,
+ "LayoutCount": 12,
+ "RecalcStyleCount": 21,
+ "LayoutDuration": 0.012584,
+ "RecalcStyleDuration": 0.054188,
+ "DevToolsCommandDuration": 0.01531,
+ "ScriptDuration": 0.070378,
+ "V8CompileDuration": 0.000054,
+ "TaskDuration": 1.079152,
+ "TaskOtherDuration": 0.926638,
+ "ThreadTime": 0.822325,
+ "ProcessTime": 3.362924,
+ "JSHeapUsedSize": 216212528,
+ "JSHeapTotalSize": 279707648,
+ "FirstMeaningfulPaint": 1844398.72813,
+ "DomContentLoaded": 1844396.214436,
+ "NavigationStart": 1844395.805343
+ },
+ "cdpAfter": {
+ "Timestamp": 1844437.585837,
+ "AudioHandlers": 0,
+ "AudioWorkletProcessors": 0,
+ "Documents": 2,
+ "Frames": 1,
+ "JSEventListeners": 5304,
+ "LayoutObjects": 9034,
+ "MediaKeySessions": 0,
+ "MediaKeys": 0,
+ "Nodes": 35734,
+ "Resources": 481,
+ "ContextLifecycleStateObservers": 40,
+ "V8PerContextDatas": 2,
+ "WorkerGlobalScopes": 2,
+ "UACSSResources": 0,
+ "RTCPeerConnections": 0,
+ "ResourceFetchers": 3,
+ "AdSubframes": 0,
+ "DetachedScriptStates": 0,
+ "ArrayBufferContents": 38,
+ "LayoutCount": 17,
+ "RecalcStyleCount": 67,
+ "LayoutDuration": 0.013643,
+ "RecalcStyleDuration": 0.147649,
+ "DevToolsCommandDuration": 0.021956,
+ "ScriptDuration": 0.198345,
+ "V8CompileDuration": 0.000054,
+ "TaskDuration": 1.571997,
+ "TaskOtherDuration": 1.19035,
+ "ThreadTime": 1.273883,
+ "ProcessTime": 4.88941,
+ "JSHeapUsedSize": 202351632,
+ "JSHeapTotalSize": 208666624,
+ "FirstMeaningfulPaint": 1844398.72813,
+ "DomContentLoaded": 1844396.214436,
+ "NavigationStart": 1844395.805343
+ },
+ "errors": [],
+ "failed": []
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/performance-comparison.json b/src/objects/omega-centauri/evidence/2026-09-20/performance-comparison.json
new file mode 100644
index 0000000000..6a52ef2a93
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/performance-comparison.json
@@ -0,0 +1,188 @@
+{
+ "measuredAt": "2026-09-20T21:39:50.350Z",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "repeatCount": 3,
+ "sourcePinsUnchanged": true,
+ "sameInitialPoseAllRepeats": true,
+ "sameFinalPoseAllRepeats": true,
+ "baselineFinalPosesEqual": true,
+ "dragSeconds": [
+ 8.192600582999999,
+ 8.131747500000001,
+ 8.11101625
+ ],
+ "baselineDragSeconds": [
+ 32.255737458,
+ 32.173033125,
+ 32.703860291000005
+ ],
+ "rafP95Milliseconds": [
+ 309.5,
+ 308.6999999999998,
+ 308.5
+ ],
+ "baselineRafP95Milliseconds": [
+ 1150,
+ 1133.4000000000015,
+ 1141.6000000000022
+ ],
+ "cdpTaskSeconds": [
+ 0.16077200000000003,
+ 0.16966199999999998,
+ 0.16791500000000004
+ ],
+ "cdpScriptSeconds": [
+ 0.07597899999999999,
+ 0.07453200000000001,
+ 0.07277800000000001
+ ],
+ "cdpStyleSeconds": [
+ 0.012311000000000002,
+ 0.011046,
+ 0.010797000000000001
+ ],
+ "longTaskCounts": [
+ 0,
+ 0,
+ 0
+ ],
+ "idleRafP95Milliseconds": [
+ 9.200000000000728,
+ 9.899999999999636,
+ 10.100000000000364
+ ],
+ "domBefore": [
+ 18068,
+ 18068,
+ 18068
+ ],
+ "domAfter": [
+ 18068,
+ 18068,
+ 18068
+ ],
+ "banks": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1,
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ }
+ ],
+ "overview": [
+ {
+ "id": "milky-way-overview",
+ "display": "none",
+ "opacity": 0,
+ "elements": 1382,
+ "planes": 1368,
+ "stars": 0,
+ "volumeOpacity": "1"
+ }
+ ],
+ "browser": "148.0.7778.96",
+ "hardware": {
+ "gpu": {
+ "devices": [
+ {
+ "vendorId": 65535,
+ "deviceId": 65535,
+ "vendorString": "Google Inc. (Google)",
+ "deviceString": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (LLVM 10.0.0) (0x0000C0DE)), SwiftShader driver-5.0.0)",
+ "driverVendor": "SwANGLE",
+ "driverVersion": "5.0.0"
+ }
+ ],
+ "auxAttributes": {
+ "amdSwitchable": false,
+ "canSupportThreadedTextureMailbox": false,
+ "directRenderingVersion": "",
+ "displayType": "ANGLE_SWIFTSHADER",
+ "glExtensions": "GL_AMD_performance_monitor GL_ANGLE_base_vertex_base_instance_shader_builtin GL_ANGLE_blob_cache GL_ANGLE_client_arrays GL_ANGLE_clip_cull_distance GL_ANGLE_compressed_texture_etc GL_ANGLE_copy_texture_3d GL_ANGLE_depth_texture GL_ANGLE_framebuffer_blit GL_ANGLE_framebuffer_multisample GL_ANGLE_get_image GL_ANGLE_get_serialized_context_string GL_ANGLE_get_tex_level_parameter GL_ANGLE_instanced_arrays GL_ANGLE_memory_object_flags GL_ANGLE_memory_size GL_ANGLE_multi_draw GL_ANGLE_pack_reverse_row_order GL_ANGLE_polygon_mode GL_ANGLE_program_binary_readiness_query GL_ANGLE_program_cache_control GL_ANGLE_read_only_depth_stencil_feedback_loops GL_ANGLE_relaxed_vertex_attribute_type GL_ANGLE_renderability_validation GL_ANGLE_request_extension GL_ANGLE_rgbx_internal_format GL_ANGLE_robust_client_memory GL_ANGLE_robust_fragment_shader_output GL_ANGLE_robust_resource_initialization GL_ANGLE_shader_pixel_local_storage GL_ANGLE_shader_pixel_local_storage_coherent GL_ANGLE_stencil_texturing GL_ANGLE_texture_compression_dxt3 GL_ANGLE_texture_compression_dxt5 GL_ANGLE_texture_multisample GL_ANGLE_texture_usage GL_ANGLE_translated_shader_source GL_ANGLE_vulkan_image GL_ANGLE_yuv_internal_format GL_APPLE_clip_distance GL_ARM_rgba8 GL_ARM_shader_framebuffer_fetch GL_ARM_shader_framebuffer_fetch_depth_stencil GL_CHROMIUM_bind_generates_resource GL_CHROMIUM_bind_uniform_location GL_CHROMIUM_copy_compressed_texture GL_CHROMIUM_copy_texture GL_CHROMIUM_lose_context GL_EXT_EGL_image_array GL_EXT_EGL_image_external_wrap_modes GL_EXT_EGL_image_storage GL_EXT_base_instance GL_EXT_blend_minmax GL_EXT_buffer_storage GL_EXT_clear_texture GL_EXT_clip_control GL_EXT_clip_cull_distance GL_EXT_color_buffer_float GL_EXT_color_buffer_half_float GL_EXT_compressed_ETC1_RGB8_sub_texture GL_EXT_conservative_depth GL_EXT_copy_image GL_EXT_debug_label GL_EXT_debug_marker GL_EXT_depth_clamp GL_EXT_discard_framebuffer GL_EXT_disjoint_timer_query GL_EXT_draw_buffers GL_EXT_draw_buffers_indexed GL_EXT_draw_elements_base_vertex GL_EXT_float_blend GL_EXT_frag_depth GL_EXT_instanced_arrays GL_EXT_map_buffer_range GL_EXT_memory_object GL_EXT_memory_object_fd GL_EXT_occlusion_query_boolean GL_EXT_polygon_offset_clamp GL_EXT_read_format_bgra GL_EXT_robustness GL_EXT_sRGB GL_EXT_sRGB_write_control GL_EXT_semaphore GL_EXT_separate_shader_objects GL_EXT_shader_framebuffer_fetch GL_EXT_shader_framebuffer_fetch_non_coherent GL_EXT_shader_non_constant_global_initializers GL_EXT_shader_texture_lod GL_EXT_shadow_samplers GL_EXT_texture_border_clamp GL_EXT_texture_compression_bptc GL_EXT_texture_compression_dxt1 GL_EXT_texture_compression_rgtc GL_EXT_texture_compression_s3tc_srgb GL_EXT_texture_cube_map_array GL_EXT_texture_filter_anisotropic GL_EXT_texture_format_BGRA8888 GL_EXT_texture_mirror_clamp_to_edge GL_EXT_texture_rg GL_EXT_texture_sRGB_R8 GL_EXT_texture_sRGB_RG8 GL_EXT_texture_sRGB_decode GL_EXT_texture_shadow_lod GL_EXT_texture_storage GL_EXT_texture_type_2_10_10_10_REV GL_EXT_unpack_subimage GL_KHR_blend_equation_advanced GL_KHR_debug GL_KHR_robust_buffer_access_behavior GL_KHR_robustness GL_KHR_texture_compression_astc_ldr GL_NV_depth_buffer_float2 GL_NV_fence GL_NV_framebuffer_blit GL_NV_pack_subimage GL_NV_pixel_buffer_object GL_NV_polygon_mode GL_NV_read_depth GL_NV_read_depth_stencil GL_NV_read_stencil GL_NV_shader_noperspective_interpolation GL_OES_EGL_image GL_OES_EGL_image_external GL_OES_EGL_image_external_essl3 GL_OES_EGL_sync GL_OES_compressed_EAC_R11_signed_texture GL_OES_compressed_EAC_R11_unsigned_texture GL_OES_compressed_EAC_RG11_signed_texture GL_OES_compressed_EAC_RG11_unsigned_texture GL_OES_compressed_ETC1_RGB8_texture GL_OES_compressed_ETC2_RGB8_texture GL_OES_compressed_ETC2_RGBA8_texture GL_OES_compressed_ETC2_punchthroughA_RGBA8_texture GL_OES_compressed_ETC2_punchthroughA_sRGB8_alpha_texture GL_OES_compressed_ETC2_sRGB8_alpha8_texture GL_OES_compressed_ETC2_sRGB8_texture GL_OES_compressed_paletted_texture GL_OES_copy_image GL_OES_depth24 GL_OES_depth32 GL_OES_depth_texture GL_OES_depth_texture_cube_map GL_OES_draw_buffers_indexed GL_OES_draw_elements_base_vertex GL_OES_element_index_uint GL_OES_fbo_render_mipmap GL_OES_get_program_binary GL_OES_mapbuffer GL_OES_packed_depth_stencil GL_OES_required_internalformat GL_OES_rgb8_rgba8 GL_OES_sample_shading GL_OES_sample_variables GL_OES_shader_multisample_interpolation GL_OES_standard_derivatives GL_OES_surfaceless_context GL_OES_texture_3D GL_OES_texture_border_clamp GL_OES_texture_cube_map_array GL_OES_texture_float GL_OES_texture_float_linear GL_OES_texture_half_float GL_OES_texture_half_float_linear GL_OES_texture_npot GL_OES_texture_stencil8 GL_OES_texture_storage_multisample_2d_array GL_OES_vertex_array_object GL_OES_vertex_half_float GL_OES_vertex_type_10_10_10_2 GL_OVR_multiview GL_OVR_multiview2 ",
+ "glImplementationParts": "(gl=egl-angle,angle=swiftshader)",
+ "glRenderer": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (LLVM 10.0.0) (0x0000C0DE)), SwiftShader driver-5.0.0)",
+ "glResetNotificationStrategy": 33362,
+ "glVendor": "Google Inc. (Google)",
+ "glVersion": "OpenGL ES 3.0 (ANGLE 2.1.27471 git hash: cc0e3572e878)",
+ "glWsExtensions": "",
+ "glWsVendor": "",
+ "glWsVersion": "",
+ "inProcessGpu": false,
+ "initializationTime": 0.025055,
+ "isAsan": false,
+ "isClangCoverage": false,
+ "jpegDecodeAcceleratorSupported": false,
+ "maxMsaaSamples": "4",
+ "optimus": false,
+ "passthroughCmdDecoder": true,
+ "pixelShaderVersion": "3.00",
+ "processCrashCount": 0,
+ "sandboxed": false,
+ "skiaBackendType": "GraphiteDawnVulkan",
+ "subpixelFontRendering": true,
+ "targetCpuBits": 64,
+ "vertexShaderVersion": "3.00",
+ "visibilityCallbackCallCount": 0
+ },
+ "featureStatus": {
+ "2d_canvas": "unavailable_software",
+ "direct_rendering_display_compositor": "disabled_off_ok",
+ "gpu_compositing": "disabled_software",
+ "multiple_raster_threads": "enabled_on",
+ "opengl": "disabled_off",
+ "rasterization": "disabled_software",
+ "raw_draw": "disabled_off_ok",
+ "skia_graphite": "disabled_off",
+ "trees_in_viz": "disabled_off",
+ "video_decode": "disabled_software",
+ "video_encode": "disabled_software",
+ "webgl": "unavailable_software",
+ "webgpu": "unavailable_software",
+ "webnn": "unavailable_software"
+ },
+ "driverBugWorkarounds": [],
+ "videoDecoding": [],
+ "videoEncoding": []
+ }
+ },
+ "endPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ },
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "sha256": "67286a0589f42d990d467d5c1e199661bc8b98b7102d152c527f34fc095e4a36"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-universe-runtime.ts",
+ "sha256": "fd0d735b8f44cd639e3c9cf5333a4f4fbef1a2c9dbbbf8d288cf59a4441dae2f"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.ts",
+ "sha256": "12a727775a8277fede7cd743642b960a30c10a7334f2e47186d97b6e8d5413f4"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-world-context.ts",
+ "sha256": "00758c8d75054092c209c68b4e311605a4a886d59fda5b36cdf86bf5d41d9ab2"
+ },
+ {
+ "path": "src/renderers/css/dist/universe.js",
+ "sha256": "a4aa3371c6836dcb1df973ab5cadbf9c9bd5d3ebb2555e9e46aff31fc2b9c520"
+ }
+ ],
+ "scriptSha256": "9afb955048abc24b3f9d8be55f86cb199fb984c59847afd7dfb133cbcaf7abee",
+ "protocolUnchanged": true,
+ "medianInputReductionPercent": 74.78976411378503,
+ "medianTaskReductionPercent": 67.33717704819814
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/r2-restore.json b/src/objects/omega-centauri/evidence/2026-09-20/r2-restore.json
new file mode 100644
index 0000000000..b57ca44d7b
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/r2-restore.json
@@ -0,0 +1,607 @@
+{
+ "status": "verified",
+ "claim": "Publication availability and exact pinned bytes; separate from scientific/visual acceptance and compact replay",
+ "root": "/private/tmp/cssearth-pr390/output/pr390-audit/app-integration-ci/omega-r2-cold-1789937540499",
+ "startedAt": "2026-09-20T20:52:20.508Z",
+ "endedAt": "2026-09-20T20:52:23.318Z",
+ "durationSeconds": 2.8095364999999997,
+ "node": "v22.23.2",
+ "platform": "darwin",
+ "arch": "arm64",
+ "inputPins": {
+ "object.json": "7846b5714f0d3c55f547ab5207914ee7ec1494c82413b650a20a3f4b91242781",
+ "runtime-assets.json": "e30940cfcec240029ada2ddd666379804e57d863087d97a7ad91be598cb0f5bf",
+ "prepared-assets.json": "a307635f2e3b99073e303b62fc7ec43ca731ea9c99a4d47df411c49e921c4fbd"
+ },
+ "restored": {
+ "installed": 64,
+ "reused": 0,
+ "skipped": 0
+ },
+ "repeat": {
+ "installed": 0,
+ "reused": 64,
+ "skipped": 0
+ },
+ "descriptorPreparedSha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813",
+ "verified": [
+ {
+ "filename": "datasets/8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb.webp",
+ "bytes": 187756,
+ "sha256": "8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb"
+ },
+ {
+ "filename": "datasets/33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1.webp",
+ "bytes": 218816,
+ "sha256": "33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1"
+ },
+ {
+ "filename": "delivery.json",
+ "bytes": 576,
+ "sha256": "f1753dcbd3c1d5d72633cccbb6bc69f61d60ca0378e6147aee8b91f04c891792"
+ },
+ {
+ "filename": "eso0844a/atlases/x.webp",
+ "bytes": 1590702,
+ "sha256": "f552913f7a8bef2d5ed213070996662a18065b969014d77d29e3dd8475718abb"
+ },
+ {
+ "filename": "eso0844a/atlases/y.webp",
+ "bytes": 1563374,
+ "sha256": "f8961a5c528dacc1a2f502990054e8d3af168b3b8bf7f9f48a45e7521b40137f"
+ },
+ {
+ "filename": "eso0844a/atlases/z.webp",
+ "bytes": 1578030,
+ "sha256": "398566ba9d6e1c0397560b042d02a02874132f08b0bfbbb8e49bf8729c4e7dfa"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00n.png",
+ "bytes": 12781,
+ "sha256": "3f30cb4a6b7bcd0eb2d629af30b01a25aede20460da48480704e8c1db5ef3759"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00p.png",
+ "bytes": 12814,
+ "sha256": "3e2914737aa86ff78b5e341bb947e6747fe69a6743d330b5221beffb1a80e668"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0n0.png",
+ "bytes": 14309,
+ "sha256": "c71c68ade95973a89bf755a981e504328e4049fd89123f611263b13ea265db2c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0nn.png",
+ "bytes": 16281,
+ "sha256": "7b727ab20e297e046c9c2b731ea947caab6ea16dd7c87d3af0e3f8119f5ee1a1"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0np.png",
+ "bytes": 16645,
+ "sha256": "d1917707b737fa9f09e0d55289b7d508e400610d2dead5296d45c7e5e02549b5"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0p0.png",
+ "bytes": 14278,
+ "sha256": "e604cd6936356ad8cebbfd4dee3e83e24fdf8343f1a22e0d96c6ccf086bb2c0b"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pn.png",
+ "bytes": 16603,
+ "sha256": "4e3817325bbb393ce73e0297f308bcff87251572f052cad6f68d5fcdaf0bf471"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pp.png",
+ "bytes": 16276,
+ "sha256": "92a50de658df291184dfd54da8bab0c6ea2a261d732da098c430873649b4e909"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n00.png",
+ "bytes": 13824,
+ "sha256": "312b5c2d0359368cce0c1cdaaff3953daad28ad476d37a9de7e4658d157be60d"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0n.png",
+ "bytes": 16267,
+ "sha256": "eba159fb949f749e0dcc84055605e056a5b1ca5456fd553eb5099c664d4e28ea"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0p.png",
+ "bytes": 16163,
+ "sha256": "9654c8617ea1a3ac39d554be1f4ffa6c13127f102201a2b2a1bb6e83c210d094"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nn0.png",
+ "bytes": 16946,
+ "sha256": "0f087f5b96fabe22db213eddf59d054cb70d280907bea79e53a203da2c9b8674"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnn.png",
+ "bytes": 18279,
+ "sha256": "a4268964eb18e91f52cfddf34cd93c3b12bc515d7c237678548a17e99d5f132e"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnp.png",
+ "bytes": 17927,
+ "sha256": "e23523b8a86bf2dfecef40a63321f186267c4cfef0bb7f5cd50a418a4fd7a09f"
+ },
+ {
+ "filename": "eso0844a/impostors/view-np0.png",
+ "bytes": 16400,
+ "sha256": "a5ad7416d1419bb7d91f23281a87283133d949754f42d65f72794ca48dc68dc6"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npn.png",
+ "bytes": 18052,
+ "sha256": "34ab6a989fee1ae995340e32377f61eb03d6d74a292f55d1b13fc6e2d86dfb06"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npp.png",
+ "bytes": 17807,
+ "sha256": "a4fba09da43abedace04a62257ad5e4438f7d3b9aabf349350ca6ba5422350d3"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p00.png",
+ "bytes": 13847,
+ "sha256": "ac1c8bb9baced6508068979a00885377abe5dd42679d55745ebbb8b69a90196f"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0n.png",
+ "bytes": 16103,
+ "sha256": "b1bd23ec644909906e35d1cf0cbf694c57396c27692d4f44a6be6b2b241200d9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0p.png",
+ "bytes": 16256,
+ "sha256": "230f731c284d627a242d5fd32ac2ac7b5243b7960c8e315c9bc8fa502675ceb5"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pn0.png",
+ "bytes": 16398,
+ "sha256": "23a9b3ab916bb65d80911319b1ddb9f6a18388d4ac9da49c75447d11afd3ffaf"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnn.png",
+ "bytes": 17852,
+ "sha256": "0647793e2f7308e5d963dd347db7342d658502484d6f2e53d88d95977100171d"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnp.png",
+ "bytes": 18068,
+ "sha256": "a088f38868a3c77048496748bee8a612e4ef1850ef252a5e4c4acd0d83242359"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pp0.png",
+ "bytes": 16994,
+ "sha256": "417ee690667fed688113ecfdf989e78039b1db4c2a75e133b34608ec2ad6fbb4"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppn.png",
+ "bytes": 17978,
+ "sha256": "7ff6ca4aa06054cc1170da2d1d17949deca83bb031e34119a43f7fb4a93c5df4"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppp.png",
+ "bytes": 18270,
+ "sha256": "f327ebf876775e779fbf4b7b5ace627f85f860b0a8f80cd16159fb593c0c849b"
+ },
+ {
+ "filename": "eso1119b/atlases/x.webp",
+ "bytes": 1781008,
+ "sha256": "b7e693615f2de5972e15e7ee2d703ec1a09ad73fed060c3cd303179cee2f3262"
+ },
+ {
+ "filename": "eso1119b/atlases/y.webp",
+ "bytes": 1725652,
+ "sha256": "f933ddebe0c18cbaf393d973a4c32fbcf0cae5e044dd585772611173329934eb"
+ },
+ {
+ "filename": "eso1119b/atlases/z.webp",
+ "bytes": 1837078,
+ "sha256": "a327f9be666bcec8b5c5b5778e0069cc75a9862ba5dd3ccc74aaa26d688eb16d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00n.png",
+ "bytes": 17775,
+ "sha256": "f2674a9bf843dfd253c3a3e13297ace857da8e5b2e4b4426286956ab296e3903"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00p.png",
+ "bytes": 17774,
+ "sha256": "3eed839f18fec65fb2b9655061fb9e27a4dee27956a642d9c04b4e49693b266a"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0n0.png",
+ "bytes": 18208,
+ "sha256": "fe2a4e3ca81034c7edd80973f71bf7dea2de68ec49e67b4619a2340e514e0865"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0nn.png",
+ "bytes": 20994,
+ "sha256": "d28ea496188c619de7051ee4587e9b1a791062a6429e47c2737260e445420e31"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0np.png",
+ "bytes": 21852,
+ "sha256": "37b9bd534fab36690564a1bf913e79a562300dfbf13c04d23da6f898d2298fcf"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0p0.png",
+ "bytes": 18165,
+ "sha256": "e7248727f96a3c77c0ed6cef064caa2c8fd58a53263a81cb9ff82761f3005588"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pn.png",
+ "bytes": 21853,
+ "sha256": "70e6bce0f8f202091242a96353d9ce15906c9c0e1b132e8fce9349d3a99872b4"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pp.png",
+ "bytes": 21017,
+ "sha256": "5323bfa51cbd316c49b5465a320c503b7bafb1c7e860af3f99563df0cca8d064"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n00.png",
+ "bytes": 18408,
+ "sha256": "0d12581cd70d9c6e8a405c32e59cf583fed79c2de31415a67d53db67647086cf"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0n.png",
+ "bytes": 21522,
+ "sha256": "2ffbecc2d78407fe8082780eae1766bd64c5fd80a472e090a9d61f3e42fa62c7"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0p.png",
+ "bytes": 21049,
+ "sha256": "c935d2479ca0888c434570561a51e4f82cf4900aebe0509704b9c5aab744c5fc"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nn0.png",
+ "bytes": 20581,
+ "sha256": "6da2999fb914f092896d3092345db2a7bb15d772ba89f245f6840b62d89717f5"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnn.png",
+ "bytes": 23128,
+ "sha256": "3a51d561f71a67f5e504550ca604b2595d289e5face6f7e6b411d5018cb39b54"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnp.png",
+ "bytes": 23076,
+ "sha256": "2b267105d2a959e4a7b766baaf3d0d2d3bd62f29b0e29d12f0e30a5a0a1fce39"
+ },
+ {
+ "filename": "eso1119b/impostors/view-np0.png",
+ "bytes": 20496,
+ "sha256": "4d5ff2dc57d9acf495f1e4f1a0dac2b2d1b6b477b87859ce0cd1a880626e3ea7"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npn.png",
+ "bytes": 23747,
+ "sha256": "b20d7118a10509f20ca8c6daac26a03545eab0430ccea9361d0c072a1e012bf6"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npp.png",
+ "bytes": 22734,
+ "sha256": "fc5d3fffd9ad8a1bbff9d858204eb791b7862eb7817532a1c86d59f84dd1c6eb"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p00.png",
+ "bytes": 18325,
+ "sha256": "4304a8f8567dcd622e952c4b8b2a5de79b99d21b8f0538f1bd8ba0b44fb10220"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0n.png",
+ "bytes": 21035,
+ "sha256": "38158cc27df1935088f4b78ff999bebef983737cf9817eab49f3c3b4f61c451c"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0p.png",
+ "bytes": 21472,
+ "sha256": "b87d11c99416f0353c4f93c75c6f33315dd70a12bd4a2ad429574e4ff2084b18"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pn0.png",
+ "bytes": 20452,
+ "sha256": "20250aae317317ca1bc6ac9418f3921e151e8a63587ebd38d3d3ea6b4467e06d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnn.png",
+ "bytes": 22755,
+ "sha256": "a5c284e57bebf454e0aa7cffa96f77319f1e001caa6b1d008189509776b4686d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnp.png",
+ "bytes": 23735,
+ "sha256": "8190a7636d3454778ee1faa611af94d930e3bfcd1d5a988e718f31a172cfaf34"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pp0.png",
+ "bytes": 20611,
+ "sha256": "de3b4315a88952ae1321715d9b249adf7b9e548c4742bf195e9a51926bd5f09e"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppn.png",
+ "bytes": 23084,
+ "sha256": "dcc980e099d2f59298ec55ec82c87040d9b4b21967d85274fde2788e16cf9dfa"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppp.png",
+ "bytes": 23136,
+ "sha256": "8744fe9f974da4598ee0c1300fc2d3cbdc330337e0f186231bc5e37c73d55781"
+ },
+ {
+ "filename": "lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ },
+ {
+ "filename": "presentation.json",
+ "bytes": 4200,
+ "sha256": "69e809ee3e10109b51e9e3a2e8f159436a7038a43693e8820da396a90e0c53cc"
+ },
+ {
+ "filename": "provenance.json",
+ "bytes": 76935,
+ "sha256": "b06370b5aeecd477765f16c6e77051e2b5d85a6bad3b5360ab1528b5cc65355f"
+ }
+ ],
+ "requests": [
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb/datasets/8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f1753dcbd3c1d5d72633cccbb6bc69f61d60ca0378e6147aee8b91f04c891792/delivery.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1/datasets/33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f552913f7a8bef2d5ed213070996662a18065b969014d77d29e3dd8475718abb/eso0844a/atlases/x.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3f30cb4a6b7bcd0eb2d629af30b01a25aede20460da48480704e8c1db5ef3759/eso0844a/impostors/view-00n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/398566ba9d6e1c0397560b042d02a02874132f08b0bfbbb8e49bf8729c4e7dfa/eso0844a/atlases/z.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f8961a5c528dacc1a2f502990054e8d3af168b3b8bf7f9f48a45e7521b40137f/eso0844a/atlases/y.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3e2914737aa86ff78b5e341bb947e6747fe69a6743d330b5221beffb1a80e668/eso0844a/impostors/view-00p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d1917707b737fa9f09e0d55289b7d508e400610d2dead5296d45c7e5e02549b5/eso0844a/impostors/view-0np.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c71c68ade95973a89bf755a981e504328e4049fd89123f611263b13ea265db2c/eso0844a/impostors/view-0n0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/92a50de658df291184dfd54da8bab0c6ea2a261d732da098c430873649b4e909/eso0844a/impostors/view-0pp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b727ab20e297e046c9c2b731ea947caab6ea16dd7c87d3af0e3f8119f5ee1a1/eso0844a/impostors/view-0nn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4e3817325bbb393ce73e0297f308bcff87251572f052cad6f68d5fcdaf0bf471/eso0844a/impostors/view-0pn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a5ad7416d1419bb7d91f23281a87283133d949754f42d65f72794ca48dc68dc6/eso0844a/impostors/view-np0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e604cd6936356ad8cebbfd4dee3e83e24fdf8343f1a22e0d96c6ccf086bb2c0b/eso0844a/impostors/view-0p0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/312b5c2d0359368cce0c1cdaaff3953daad28ad476d37a9de7e4658d157be60d/eso0844a/impostors/view-n00.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/eba159fb949f749e0dcc84055605e056a5b1ca5456fd553eb5099c664d4e28ea/eso0844a/impostors/view-n0n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9654c8617ea1a3ac39d554be1f4ffa6c13127f102201a2b2a1bb6e83c210d094/eso0844a/impostors/view-n0p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a4268964eb18e91f52cfddf34cd93c3b12bc515d7c237678548a17e99d5f132e/eso0844a/impostors/view-nnn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ac1c8bb9baced6508068979a00885377abe5dd42679d55745ebbb8b69a90196f/eso0844a/impostors/view-p00.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a4fba09da43abedace04a62257ad5e4438f7d3b9aabf349350ca6ba5422350d3/eso0844a/impostors/view-npp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0f087f5b96fabe22db213eddf59d054cb70d280907bea79e53a203da2c9b8674/eso0844a/impostors/view-nn0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/230f731c284d627a242d5fd32ac2ac7b5243b7960c8e315c9bc8fa502675ceb5/eso0844a/impostors/view-p0p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e23523b8a86bf2dfecef40a63321f186267c4cfef0bb7f5cd50a418a4fd7a09f/eso0844a/impostors/view-nnp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b1bd23ec644909906e35d1cf0cbf694c57396c27692d4f44a6be6b2b241200d9/eso0844a/impostors/view-p0n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0647793e2f7308e5d963dd347db7342d658502484d6f2e53d88d95977100171d/eso0844a/impostors/view-pnn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/34ab6a989fee1ae995340e32377f61eb03d6d74a292f55d1b13fc6e2d86dfb06/eso0844a/impostors/view-npn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/417ee690667fed688113ecfdf989e78039b1db4c2a75e133b34608ec2ad6fbb4/eso0844a/impostors/view-pp0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f327ebf876775e779fbf4b7b5ace627f85f860b0a8f80cd16159fb593c0c849b/eso0844a/impostors/view-ppp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/23a9b3ab916bb65d80911319b1ddb9f6a18388d4ac9da49c75447d11afd3ffaf/eso0844a/impostors/view-pn0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a088f38868a3c77048496748bee8a612e4ef1850ef252a5e4c4acd0d83242359/eso0844a/impostors/view-pnp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7ff6ca4aa06054cc1170da2d1d17949deca83bb031e34119a43f7fb4a93c5df4/eso0844a/impostors/view-ppn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f2674a9bf843dfd253c3a3e13297ace857da8e5b2e4b4426286956ab296e3903/eso1119b/impostors/view-00n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a327f9be666bcec8b5c5b5778e0069cc75a9862ba5dd3ccc74aaa26d688eb16d/eso1119b/atlases/z.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3eed839f18fec65fb2b9655061fb9e27a4dee27956a642d9c04b4e49693b266a/eso1119b/impostors/view-00p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/70e6bce0f8f202091242a96353d9ce15906c9c0e1b132e8fce9349d3a99872b4/eso1119b/impostors/view-0pn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/fe2a4e3ca81034c7edd80973f71bf7dea2de68ec49e67b4619a2340e514e0865/eso1119b/impostors/view-0n0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f933ddebe0c18cbaf393d973a4c32fbcf0cae5e044dd585772611173329934eb/eso1119b/atlases/y.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b7e693615f2de5972e15e7ee2d703ec1a09ad73fed060c3cd303179cee2f3262/eso1119b/atlases/x.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5323bfa51cbd316c49b5465a320c503b7bafb1c7e860af3f99563df0cca8d064/eso1119b/impostors/view-0pp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e7248727f96a3c77c0ed6cef064caa2c8fd58a53263a81cb9ff82761f3005588/eso1119b/impostors/view-0p0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0d12581cd70d9c6e8a405c32e59cf583fed79c2de31415a67d53db67647086cf/eso1119b/impostors/view-n00.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2b267105d2a959e4a7b766baaf3d0d2d3bd62f29b0e29d12f0e30a5a0a1fce39/eso1119b/impostors/view-nnp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d28ea496188c619de7051ee4587e9b1a791062a6429e47c2737260e445420e31/eso1119b/impostors/view-0nn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/37b9bd534fab36690564a1bf913e79a562300dfbf13c04d23da6f898d2298fcf/eso1119b/impostors/view-0np.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6da2999fb914f092896d3092345db2a7bb15d772ba89f245f6840b62d89717f5/eso1119b/impostors/view-nn0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2ffbecc2d78407fe8082780eae1766bd64c5fd80a472e090a9d61f3e42fa62c7/eso1119b/impostors/view-n0n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3a51d561f71a67f5e504550ca604b2595d289e5face6f7e6b411d5018cb39b54/eso1119b/impostors/view-nnn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c935d2479ca0888c434570561a51e4f82cf4900aebe0509704b9c5aab744c5fc/eso1119b/impostors/view-n0p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b20d7118a10509f20ca8c6daac26a03545eab0430ccea9361d0c072a1e012bf6/eso1119b/impostors/view-npn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/fc5d3fffd9ad8a1bbff9d858204eb791b7862eb7817532a1c86d59f84dd1c6eb/eso1119b/impostors/view-npp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/38158cc27df1935088f4b78ff999bebef983737cf9817eab49f3c3b4f61c451c/eso1119b/impostors/view-p0n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b87d11c99416f0353c4f93c75c6f33315dd70a12bd4a2ad429574e4ff2084b18/eso1119b/impostors/view-p0p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/de3b4315a88952ae1321715d9b249adf7b9e548c4742bf195e9a51926bd5f09e/eso1119b/impostors/view-pp0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4304a8f8567dcd622e952c4b8b2a5de79b99d21b8f0538f1bd8ba0b44fb10220/eso1119b/impostors/view-p00.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4d5ff2dc57d9acf495f1e4f1a0dac2b2d1b6b477b87859ce0cd1a880626e3ea7/eso1119b/impostors/view-np0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a5c284e57bebf454e0aa7cffa96f77319f1e001caa6b1d008189509776b4686d/eso1119b/impostors/view-pnn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813/lenses.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/20250aae317317ca1bc6ac9418f3921e151e8a63587ebd38d3d3ea6b4467e06d/eso1119b/impostors/view-pn0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8190a7636d3454778ee1faa611af94d930e3bfcd1d5a988e718f31a172cfaf34/eso1119b/impostors/view-pnp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/dcc980e099d2f59298ec55ec82c87040d9b4b21967d85274fde2788e16cf9dfa/eso1119b/impostors/view-ppn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/69e809ee3e10109b51e9e3a2e8f159436a7038a43693e8820da396a90e0c53cc/presentation.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b06370b5aeecd477765f16c6e77051e2b5d85a6bad3b5360ab1528b5cc65355f/provenance.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8744fe9f974da4598ee0c1300fc2d3cbdc330337e0f186231bc5e37c73d55781/eso1119b/impostors/view-ppp.png",
+ "status": 200
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/shell-tests.tap b/src/objects/omega-centauri/evidence/2026-09-20/shell-tests.tap
new file mode 100644
index 0000000000..7d953b6373
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/shell-tests.tap
@@ -0,0 +1,3845 @@
+TAP version 13
+# Subtest: an object without runtime-assets.json has an empty asset map
+ok 1 - an object without runtime-assets.json has an empty asset map
+ ---
+ duration_ms: 14.885042
+ type: 'test'
+ ...
+# Subtest: an unreadable runtime-assets.json still fails the build
+ok 2 - an unreadable runtime-assets.json still fails the build
+ ---
+ duration_ms: 4.092416
+ type: 'test'
+ ...
+# Subtest: Saturn lists every source moon and only links registered scene objects
+ok 3 - Saturn lists every source moon and only links registered scene objects
+ ---
+ duration_ms: 1.440209
+ type: 'test'
+ ...
+# Subtest: other planets retain their existing navigable moon entries
+ok 4 - other planets retain their existing navigable moon entries
+ ---
+ duration_ms: 0.53725
+ type: 'test'
+ ...
+# Subtest: moon catalogues reject mismatched totals and duplicate identities
+ok 5 - moon catalogues reject mismatched totals and duplicate identities
+ ---
+ duration_ms: 0.320791
+ type: 'test'
+ ...
+# Subtest: every planet uses the complete confirmed catalogue count
+ok 6 - every planet uses the complete confirmed catalogue count
+ ---
+ duration_ms: 1.049959
+ type: 'test'
+ ...
+# Subtest: childless moons offer their parent and other available moons
+ok 7 - childless moons offer their parent and other available moons
+ ---
+ duration_ms: 0.911125
+ type: 'test'
+ ...
+# Subtest: parents keep their complete moon catalogue and childless primary bodies omit the empty tab
+ok 8 - parents keep their complete moon catalogue and childless primary bodies omit the empty tab
+ ---
+ duration_ms: 0.692333
+ type: 'test'
+ ...
+# Subtest: overview charts retain every supplied spectrum sample across the registry
+ok 9 - overview charts retain every supplied spectrum sample across the registry
+ ---
+ duration_ms: 190.200583
+ type: 'test'
+ ...
+# Subtest: overview measurements use the body reference radius and catalogue distance
+ok 10 - overview measurements use the body reference radius and catalogue distance
+ ---
+ duration_ms: 0.439833
+ type: 'test'
+ ...
+# Subtest: compact spectra reject unordered or nonfinite samples
+ok 11 - compact spectra reject unordered or nonfinite samples
+ ---
+ duration_ms: 0.388959
+ type: 'test'
+ ...
+# Subtest: no module reachable from the browser entry points has a static node: import
+ok 12 - no module reachable from the browser entry points has a static node: import
+ ---
+ duration_ms: 1519.312375
+ type: 'test'
+ ...
+# Subtest: mutation check: a static node: import reachable from an entry is caught
+ok 13 - mutation check: a static node: import reachable from an entry is caught
+ ---
+ duration_ms: 6.235708
+ type: 'test'
+ ...
+# Subtest: mutation check: a node: import reached only through a dynamic import() is not flagged
+ok 14 - mutation check: a node: import reached only through a dynamic import() is not flagged
+ ---
+ duration_ms: 3.55475
+ type: 'test'
+ ...
+# Subtest: every object mounts one canonical high-density image bank
+not ok 15 - every object mounts one canonical high-density image bank
+ ---
+ duration_ms: 18857.998958
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/canonical-image-density.test.mts:12:1'
+ failureType: 'testCodeFailure'
+ error: 'haumea: high-density startup assets'
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected: true
+ actual: false
+ operator: '=='
+ stack: |-
+ TestContext. (file:///private/tmp/cssearth-pr390/site/test/canonical-image-density.test.mts:28:12)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3)
+ ...
+# Subtest: the shared shell does not choose image assets by device DPR
+ok 16 - the shared shell does not choose image assets by device DPR
+ ---
+ duration_ms: 3.419584
+ type: 'test'
+ ...
+# Subtest: loadCatalogueFragment fetches, verifies and returns the parsed list
+ok 17 - loadCatalogueFragment fetches, verifies and returns the parsed list
+ ---
+ duration_ms: 35.701708
+ type: 'test'
+ ...
+# Subtest: loadCatalogueFragment rejects a failed request
+ok 18 - loadCatalogueFragment rejects a failed request
+ ---
+ duration_ms: 0.392416
+ type: 'test'
+ ...
+# Subtest: loadCatalogueFragment rejects a network failure
+ok 19 - loadCatalogueFragment rejects a network failure
+ ---
+ duration_ms: 0.115959
+ type: 'test'
+ ...
+# Subtest: loadCatalogueFragment rejects a size drift
+ok 20 - loadCatalogueFragment rejects a size drift
+ ---
+ duration_ms: 2.1505
+ type: 'test'
+ ...
+# Subtest: loadCatalogueFragment rejects a hash mismatch
+ok 21 - loadCatalogueFragment rejects a hash mismatch
+ ---
+ duration_ms: 0.438375
+ type: 'test'
+ ...
+# Subtest: loadCatalogueFragment rejects content missing the list
+ok 22 - loadCatalogueFragment rejects content missing the list
+ ---
+ duration_ms: 1.0975
+ type: 'test'
+ ...
+# Subtest: scheduleWhenIdle prefers requestIdleCallback and falls back to a timer
+ok 23 - scheduleWhenIdle prefers requestIdleCallback and falls back to a timer
+ ---
+ duration_ms: 0.155791
+ type: 'test'
+ ...
+# Subtest: parseCatalogueFragmentPin accepts a well-formed pin and rejects everything else
+ok 24 - parseCatalogueFragmentPin accepts a well-formed pin and rejects everything else
+ ---
+ duration_ms: 1.120291
+ type: 'test'
+ ...
+# Subtest: readCatalogueFragmentPin reads a dataset host and never throws
+ok 25 - readCatalogueFragmentPin reads a dataset host and never throws
+ ---
+ duration_ms: 0.127833
+ type: 'test'
+ ...
+# Subtest: hidden charts schedule no work; visible charts batch reads, retain corrections, and retire cleanly
+ok 26 - hidden charts schedule no work; visible charts batch reads, retain corrections, and retire cleanly
+ ---
+ duration_ms: 3.071209
+ type: 'test'
+ ...
+# Subtest: the standard browser launch stays unchanged unless explicitly opted in
+ok 27 - the standard browser launch stays unchanged unless explicitly opted in
+ ---
+ duration_ms: 0.866542
+ type: 'test'
+ ...
+# Subtest: the Chrome wrapper retains logs and arguments while preserving both CDP pipes
+ok 28 - the Chrome wrapper retains logs and arguments while preserving both CDP pipes
+ ---
+ duration_ms: 540.719375
+ type: 'test'
+ ...
+# Subtest: every registered dataset has a specific published title, independent of category and source-link availability
+ok 29 - every registered dataset has a specific published title, independent of category and source-link availability
+ ---
+ duration_ms: 521.245459
+ type: 'test'
+ ...
+# Subtest: missing titles, category labels, repeated summaries and missing summaries are rejected
+ok 30 - missing titles, category labels, repeated summaries and missing summaries are rejected
+ ---
+ duration_ms: 0.346375
+ type: 'test'
+ ...
+# Subtest: Mars elevation and thermal infrared select their own missions
+ok 31 - Mars elevation and thermal infrared select their own missions
+ ---
+ duration_ms: 0.723958
+ type: 'test'
+ ...
+# Subtest: multi-input imagery lists its mission once and a cutaway does not invent one
+ok 32 - multi-input imagery lists its mission once and a cutaway does not invent one
+ ---
+ duration_ms: 0.0935
+ type: 'test'
+ ...
+# Subtest: unresolved capture attribution names the credited group without guessing a mission
+ok 33 - unresolved capture attribution names the credited group without guessing a mission
+ ---
+ duration_ms: 0.101875
+ type: 'test'
+ ...
+# Subtest: an unknown dataset does not inherit another dataset’s contributors
+ok 34 - an unknown dataset does not inherit another dataset’s contributors
+ ---
+ duration_ms: 0.094
+ type: 'test'
+ ...
+# Subtest: capture attribution follows the selected dataset, including derived maps
+ok 35 - capture attribution follows the selected dataset, including derived maps
+ ---
+ duration_ms: 23.490167
+ type: 'test'
+ ...
+# Subtest: Didymos LUKE frames keep their DART mission credit; stripping capture removes it
+ok 36 - Didymos LUKE frames keep their DART mission credit; stripping capture removes it
+ ---
+ duration_ms: 4.512
+ type: 'test'
+ ...
+# Subtest: multi-mission composites preserve every evidenced contributor without repeating dataset destinations
+ok 37 - multi-mission composites preserve every evidenced contributor without repeating dataset destinations
+ ---
+ duration_ms: 3.850667
+ type: 'test'
+ ...
+# Subtest: schematic and synthetic views do not inherit observations from their outer texture
+ok 38 - schematic and synthetic views do not inherit observations from their outer texture
+ ---
+ duration_ms: 120.438666
+ type: 'test'
+ ...
+# Subtest: every migrated capture stays bound to its source; the full prepared graph is deterministic
+ok 39 - every migrated capture stays bound to its source; the full prepared graph is deterministic
+ ---
+ duration_ms: 5613.996792
+ type: 'test'
+ ...
+# Subtest: OSIRIS-REx retains Bennu observations and successor participation creates no APEX observations
+ok 40 - OSIRIS-REx retains Bennu observations and successor participation creates no APEX observations
+ ---
+ duration_ms: 0.197125
+ type: 'test'
+ ...
+# Subtest: approved artwork and emblems retain source bytes, dimensions and transparency
+ok 41 - approved artwork and emblems retain source bytes, dimensions and transparency
+ ---
+ duration_ms: 78.616542
+ type: 'test'
+ ...
+# Subtest: unknown identities, impossible capture pairs, stale lenses and damaged indexes fail closed
+ok 42 - unknown identities, impossible capture pairs, stale lenses and damaged indexes fail closed
+ ---
+ duration_ms: 30.095166
+ type: 'test'
+ ...
+# Subtest: native selection replaces only the existing prepared presentation and selected controls
+ok 43 - native selection replaces only the existing prepared presentation and selected controls
+ ---
+ duration_ms: 279.372583
+ type: 'test'
+ ...
+# Subtest: invalid requests and corrupt prepared bytes cannot publish another dataset
+ok 44 - invalid requests and corrupt prepared bytes cannot publish another dataset
+ ---
+ duration_ms: 36.256542
+ type: 'test'
+ ...
+# Subtest: Netlify handles dataset and combined search queries without intercepting static assets
+ok 45 - Netlify handles dataset and combined search queries without intercepting static assets
+ ---
+ duration_ms: 0.703291
+ type: 'test'
+ ...
+# Subtest: a native search-only Earth selection fetches the base catalogue and exactly one detail bank
+ok 46 - a native search-only Earth selection fetches the base catalogue and exactly one detail bank
+ ---
+ duration_ms: 360.71975
+ type: 'test'
+ ...
+# Subtest: native query selections preserve view state and unrelated anchors; obsolete fragments do not select a dataset
+ok 47 - native query selections preserve view state and unrelated anchors; obsolete fragments do not select a dataset
+ ---
+ duration_ms: 1.099542
+ type: 'test'
+ ...
+# Subtest: ambiguous and malformed dataset selections are rejected before publication
+ok 48 - ambiguous and malformed dataset selections are rejected before publication
+ ---
+ duration_ms: 0.150833
+ type: 'test'
+ ...
+# Subtest: diagnostic lookup requires the current published identity and cleanup preserves a replacement owner
+ok 49 - diagnostic lookup requires the current published identity and cleanup preserves a replacement owner
+ ---
+ duration_ms: 0.662125
+ type: 'test'
+ ...
+# Subtest: records one correlated run, copies changing camera state, and is idle after stop
+ok 50 - records one correlated run, copies changing camera state, and is idle after stop
+ ---
+ duration_ms: 13.473042
+ type: 'test'
+ ...
+# Subtest: a failed diagnostic read is captured without interrupting the application
+ok 51 - a failed diagnostic read is captured without interrupting the application
+ ---
+ duration_ms: 0.561333
+ type: 'test'
+ ...
+# Subtest: rail switches the retained About panel without storing or mounting a scene
+ok 52 - rail switches the retained About panel without storing or mounting a scene
+ ---
+ duration_ms: 1.409083
+ type: 'test'
+ ...
+# Subtest: Escape restores a visible keyboard focus target
+ok 53 - Escape restores a visible keyboard focus target
+ ---
+ duration_ms: 0.3935
+ type: 'test'
+ ...
+# Subtest: search stays visible in About and Settings and typing returns to the results
+ok 54 - search stays visible in About and Settings and typing returns to the results
+ ---
+ duration_ms: 0.190375
+ type: 'test'
+ ...
+# Subtest: destroy resets retained markup and remount does not duplicate listeners
+ok 55 - destroy resets retained markup and remount does not duplicate listeners
+ ---
+ duration_ms: 0.222208
+ type: 'test'
+ ...
+# Subtest: an incomplete rail fails clearly
+ok 56 - an incomplete rail fails clearly
+ ---
+ duration_ms: 1.252708
+ type: 'test'
+ ...
+# Subtest: orders comparable facts before stable planet-specific facts
+ok 57 - orders comparable facts before stable planet-specific facts
+ ---
+ duration_ms: 0.833
+ type: 'test'
+ ...
+# Subtest: keeps unknown facts in their authored order
+ok 58 - keeps unknown facts in their authored order
+ ---
+ duration_ms: 0.084542
+ type: 'test'
+ ...
+# Subtest: prioritizes a satellite's own orbit over its parent's solar orbit
+ok 59 - prioritizes a satellite's own orbit over its parent's solar orbit
+ ---
+ duration_ms: 0.169375
+ type: 'test'
+ ...
+# Subtest: rejects facts without unique semantic ids
+ok 60 - rejects facts without unique semantic ids
+ ---
+ duration_ms: 0.206708
+ type: 'test'
+ ...
+# Subtest: an initial galaxy focus retains its transitive positioned and unpositioned physical hosts
+ok 61 - an initial galaxy focus retains its transitive positioned and unpositioned physical hosts
+ ---
+ duration_ms: 4.466708
+ type: 'test'
+ ...
+# Subtest: initial focus host closure fails on a missing host and a cycle
+ok 62 - initial focus host closure fails on a missing host and a cycle
+ ---
+ duration_ms: 1.569708
+ type: 'test'
+ ...
+# Subtest: initial focus requires an explicit selected identity present in the parsed fragment
+ok 63 - initial focus requires an explicit selected identity present in the parsed fragment
+ ---
+ duration_ms: 2.508166
+ type: 'test'
+ ...
+# Subtest: a source-owned globular cluster is discovered, classified and searched through the shared focus route
+ok 64 - a source-owned globular cluster is discovered, classified and searched through the shared focus route
+ ---
+ duration_ms: 6.299959
+ type: 'test'
+ ...
+# Subtest: analytics ignores local visits and loads on css.earth
+ok 65 - analytics ignores local visits and loads on css.earth
+ ---
+ duration_ms: 1.834875
+ type: 'test'
+ ...
+# Subtest: in-flight loader shares concurrent work but releases successful values
+ok 66 - in-flight loader shares concurrent work but releases successful values
+ ---
+ duration_ms: 1.727917
+ type: 'test'
+ ...
+# Subtest: in-flight loader releases failures so a later request can retry
+ok 67 - in-flight loader releases failures so a later request can retry
+ ---
+ duration_ms: 0.275166
+ type: 'test'
+ ...
+# Subtest: query-specific views withhold static object facts until the requested context is ready
+ok 68 - query-specific views withhold static object facts until the requested context is ready
+ ---
+ duration_ms: 2.020417
+ type: 'test'
+ ...
+# Subtest: the LMC bank regenerates from its compact finite-emission inputs exactly as the promotion baked it
+ok 69 - the LMC bank regenerates from its compact finite-emission inputs exactly as the promotion baked it
+ ---
+ duration_ms: 608.23675
+ type: 'test'
+ ...
+# Subtest: objects without lenses or extra settings retain mandatory browser proof
+ok 70 - objects without lenses or extra settings retain mandatory browser proof
+ ---
+ duration_ms: 6.43125
+ type: 'test'
+ ...
+# Subtest: declared content requires exact lens and setting DOM before optional checks
+ok 71 - declared content requires exact lens and setting DOM before optional checks
+ ---
+ duration_ms: 1.956958
+ type: 'test'
+ ...
+# Subtest: optional controls never excuse a missing shared panel or Motion
+ok 72 - optional controls never excuse a missing shared panel or Motion
+ ---
+ duration_ms: 0.358459
+ type: 'test'
+ ...
+# Subtest: declared lenses require matching race IDs, and complete retained coverage
+ok 73 - declared lenses require matching race IDs, and complete retained coverage
+ ---
+ duration_ms: 0.328792
+ type: 'test'
+ ...
+# Subtest: a single supplied lens keeps interaction proof without inventing a race
+ok 74 - a single supplied lens keeps interaction proof without inventing a race
+ ---
+ duration_ms: 0.119292
+ type: 'test'
+ ...
+# Subtest: all objects retain browser evidence for the controls they support
+not ok 75 - all objects retain browser evidence for the controls they support
+ ---
+ duration_ms: 713.144916
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/load-browser-profile.test.mts:101:1'
+ failureType: 'testCodeFailure'
+ error: 'ryugu: browser profile must declare retained interaction evidence'
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected: true
+ actual: false
+ operator: '=='
+ stack: |-
+ validatePlanetBrowserProfile (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.mts:51:10)
+ loadPlanetBrowserProfile (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.mts:19:10)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.test.mts:103:21)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: unsupported objects fail before legacy implementation paths are considered
+ok 76 - unsupported objects fail before legacy implementation paths are considered
+ ---
+ duration_ms: 0.602291
+ type: 'test'
+ ...
+# Subtest: only a later fully opaque marker covers a complete earlier marker
+ok 77 - only a later fully opaque marker covers a complete earlier marker
+ ---
+ duration_ms: 1.035333
+ type: 'test'
+ ...
+# Subtest: edge contact, motion out of coverage and responsive raster guards keep drawing
+ok 78 - edge contact, motion out of coverage and responsive raster guards keep drawing
+ ---
+ duration_ms: 0.142417
+ type: 'test'
+ ...
+# Subtest: prepared minimap index includes every source point once without changing DOM order
+ok 79 - prepared minimap index includes every source point once without changing DOM order
+ ---
+ duration_ms: 1.999459
+ type: 'test'
+ ...
+# Subtest: X range keeps inclusive duplicate boundaries and handles empty/disjoint ranges
+ok 80 - X range keeps inclusive duplicate boundaries and handles empty/disjoint ranges
+ ---
+ duration_ms: 0.249125
+ type: 'test'
+ ...
+# Subtest: indexed candidates preserve exact 3D visible points across physical scales and off-origin views
+ok 81 - indexed candidates preserve exact 3D visible points across physical scales and off-origin views
+ ---
+ duration_ms: 96.529708
+ type: 'test'
+ ...
+# Subtest: minimap retains the principal Local Group galaxies and published cluster positions
+ok 82 - minimap retains the principal Local Group galaxies and published cluster positions
+ ---
+ duration_ms: 9.617166
+ type: 'test'
+ ...
+# Subtest: the minimap is not downloaded, built or projected while its setting is off
+ok 83 - the minimap is not downloaded, built or projected while its setting is off
+ ---
+ duration_ms: 2.201
+ type: 'test'
+ ...
+# Subtest: turning the setting on builds the minimap once with the current focus, hidden bodies and camera
+ok 84 - turning the setting on builds the minimap once with the current focus, hidden bodies and camera
+ ---
+ duration_ms: 0.944125
+ type: 'test'
+ ...
+# Subtest: off stops projection and keeps the mounted minimap; on draws the latest camera without another download
+ok 85 - off stops projection and keeps the mounted minimap; on draws the latest camera without another download
+ ---
+ duration_ms: 1.082167
+ type: 'test'
+ ...
+# Subtest: a world destroyed during the download never builds the minimap
+ok 86 - a world destroyed during the download never builds the minimap
+ ---
+ duration_ms: 0.465875
+ type: 'test'
+ ...
+# Subtest: a failed download is reported and the next switch on tries again
+ok 87 - a failed download is reported and the next switch on tries again
+ ---
+ duration_ms: 1.71575
+ type: 'test'
+ ...
+# Subtest: prepared unavailable moon labels cover proper names and match pinned Horizons vectors
+ok 88 - prepared unavailable moon labels cover proper names and match pinned Horizons vectors
+ ---
+ duration_ms: 15.768375
+ type: 'test'
+ ...
+# Subtest: designation-only moons stay in the sidebar and do not become scene labels
+ok 89 - designation-only moons stay in the sidebar and do not become scene labels
+ ---
+ duration_ms: 0.934625
+ type: 'test'
+ ...
+# Subtest: disabled captions respect foreground labels, planet occlusion and overview scale
+ok 90 - disabled captions respect foreground labels, planet occlusion and overview scale
+ ---
+ duration_ms: 1.188459
+ type: 'test'
+ ...
+# Subtest: major moon orbits remain enabled and minor moon orbits are suppressed across planets
+ok 91 - major moon orbits remain enabled and minor moon orbits are suppressed across planets
+ ---
+ duration_ms: 0.186542
+ type: 'test'
+ ...
+# Subtest: disabled captions keep admission through small zoom reversals
+ok 92 - disabled captions keep admission through small zoom reversals
+ ---
+ duration_ms: 18.606542
+ type: 'test'
+ ...
+# Subtest: an intent-fetched fragment is requested once, shared by preview and content, and bounded
+ok 93 - an intent-fetched fragment is requested once, shared by preview and content, and bounded
+ ---
+ duration_ms: 18.070083
+ type: 'test'
+ ...
+# Subtest: every module copy in a window reaches the same fragment cache
+ok 94 - every module copy in a window reaches the same fragment cache
+ ---
+ duration_ms: 1.356625
+ type: 'test'
+ ...
+# Subtest: failed, mismatched and cancelled requests never poison the shared fragment
+ok 95 - failed, mismatched and cancelled requests never poison the shared fragment
+ ---
+ duration_ms: 0.7745
+ type: 'test'
+ ...
+# Subtest: hover, focus and world hover prefetch registry routes after a dwell; a press starts at once
+ok 96 - hover, focus and world hover prefetch registry routes after a dwell; a press starts at once
+ ---
+ duration_ms: 0.787167
+ type: 'test'
+ ...
+# Subtest: supersession revokes commit authority before abort and cleanup, including late resources
+ok 97 - supersession revokes commit authority before abort and cleanup, including late resources
+ ---
+ duration_ms: 0.856042
+ type: 'test'
+ ...
+# Subtest: arrival settles once without aborting the handed-off scene; failures abort
+ok 98 - arrival settles once without aborting the handed-off scene; failures abort
+ ---
+ duration_ms: 0.203375
+ type: 'test'
+ ...
+# Subtest: every prepared spatial subject and every scene has exactly one searchable destination
+ok 99 - every prepared spatial subject and every scene has exactly one searchable destination
+ ---
+ duration_ms: 284.529167
+ type: 'test'
+ ...
+# Subtest: distance display and order use the prepared position, never the legacy orbital reference
+ok 100 - distance display and order use the prepared position, never the legacy orbital reference
+ ---
+ duration_ms: 346.0285
+ type: 'test'
+ ...
+# Subtest: galaxy citations resolve across the full prepared catalogue, including the separate SMC paper
+ok 101 - galaxy citations resolve across the full prepared catalogue, including the separate SMC paper
+ ---
+ duration_ms: 11.171292
+ type: 'test'
+ ...
+# Subtest: physical hosts remain distinct from scene hosts and M45 retains its measured subject
+ok 102 - physical hosts remain distinct from scene hosts and M45 retains its measured subject
+ ---
+ duration_ms: 5.643125
+ type: 'test'
+ ...
+# Subtest: adding and reordering bodies preserves existing marker bytes
+not ok 103 - adding and reordering bodies preserves existing marker bytes
+ ---
+ duration_ms: 910.173
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:33:1'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, open '/private/tmp/cssearth-pr390/src/objects/sun/source/sdo/continuum/CR2311/20260513_000000_1024_HMIIC.jpg'"
+ code: 'ENOENT'
+ stack: |-
+ async open (node:internal/fs/promises:639:25)
+ async readFile (node:internal/fs/promises:1252:14)
+ async validateMarkerSourceBytes (file:///private/tmp/cssearth-pr390/src/navigation/marker-recipe.mts:77:17)
+ async readMarkerImage (file:///private/tmp/cssearth-pr390/src/navigation/marker-recipe.mts:89:17)
+ async renderMarker (file:///private/tmp/cssearth-pr390/src/navigation/marker-recipe.mts:103:15)
+ async prepareBodyMarkers (file:///private/tmp/cssearth-pr390/tools/prepare-navigation.mts:290:20)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:39:3)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3)
+ ...
+# Subtest: metadata-only preparation does not replace or remove images
+ok 104 - metadata-only preparation does not replace or remove images
+ ---
+ duration_ms: 72.342791
+ type: 'test'
+ ...
+# Subtest: composes every orbiting-object marker descriptor in catalog order
+ok 105 - composes every orbiting-object marker descriptor in catalog order
+ ---
+ duration_ms: 649.774417
+ type: 'test'
+ ...
+# Subtest: body marker atlases preserve every visible prepared tile pixel exactly
+ok 106 - body marker atlases preserve every visible prepared tile pixel exactly
+ ---
+ duration_ms: 2714.397584
+ type: 'test'
+ ...
+# Subtest: reproduces the checked-in body images and utility markers
+not ok 107 - reproduces the checked-in body images and utility markers
+ ---
+ duration_ms: 286.432458
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:110:1'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, open '/private/tmp/cssearth-pr390/src/objects/sun/source/sdo/continuum/CR2311/20260513_000000_1024_HMIIC.jpg'"
+ code: 'ENOENT'
+ stack: |-
+ async open (node:internal/fs/promises:639:25)
+ async readFile (node:internal/fs/promises:1252:14)
+ async validateMarkerSourceBytes (file:///private/tmp/cssearth-pr390/src/navigation/marker-recipe.mts:77:17)
+ async readMarkerImage (file:///private/tmp/cssearth-pr390/src/navigation/marker-recipe.mts:89:17)
+ async renderMarker (file:///private/tmp/cssearth-pr390/src/navigation/marker-recipe.mts:103:15)
+ async prepareBodyMarkers (file:///private/tmp/cssearth-pr390/tools/prepare-navigation.mts:290:20)
+ async renderNavigation (file:///private/tmp/cssearth-pr390/tools/prepare-navigation.mts:300:3)
+ async prepareNavigation (file:///private/tmp/cssearth-pr390/tools/prepare-navigation.mts:99:72)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:114:3)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ ...
+# Subtest: failed object source preserves accepted files or recoverable backups outside public
+not ok 108 - failed object source preserves accepted files or recoverable backups outside public
+ ---
+ duration_ms: 271.276334
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:145:3'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, copyfile '/private/tmp/cssearth-pr390/src/objects/sun/source/sdo/continuum/CR2311/20260513_000000_1024_HMIIC.jpg' -> '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/cssearth-navigation-failure-3Xi7xT/src/objects/new-body/source/source.jpg'"
+ code: 'ENOENT'
+ stack: |-
+ async copyFile (node:internal/fs/promises:621:10)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:159:5)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: failed late utility source preserves accepted files or recoverable backups outside public
+not ok 109 - failed late utility source preserves accepted files or recoverable backups outside public
+ ---
+ duration_ms: 215.718792
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:145:3'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, copyfile '/private/tmp/cssearth-pr390/src/objects/sun/source/sdo/continuum/CR2311/20260513_000000_1024_HMIIC.jpg' -> '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/cssearth-navigation-failure-Jbvc3x/src/objects/new-body/source/source.jpg'"
+ code: 'ENOENT'
+ stack: |-
+ async copyFile (node:internal/fs/promises:621:10)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:159:5)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: failed publication preserves accepted files or recoverable backups outside public
+not ok 110 - failed publication preserves accepted files or recoverable backups outside public
+ ---
+ duration_ms: 197.857208
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:145:3'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, copyfile '/private/tmp/cssearth-pr390/src/objects/sun/source/sdo/continuum/CR2311/20260513_000000_1024_HMIIC.jpg' -> '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/cssearth-navigation-failure-mEvfO7/src/objects/new-body/source/source.jpg'"
+ code: 'ENOENT'
+ stack: |-
+ async copyFile (node:internal/fs/promises:621:10)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:159:5)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: failed rollback preserves accepted files or recoverable backups outside public
+not ok 111 - failed rollback preserves accepted files or recoverable backups outside public
+ ---
+ duration_ms: 183.2325
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:145:3'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, copyfile '/private/tmp/cssearth-pr390/src/objects/sun/source/sdo/continuum/CR2311/20260513_000000_1024_HMIIC.jpg' -> '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/cssearth-navigation-failure-Ka77kn/src/objects/new-body/source/source.jpg'"
+ code: 'ENOENT'
+ stack: |-
+ async copyFile (node:internal/fs/promises:621:10)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/navigation-preparation.test.mts:159:5)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: cross-device moves copy and unlink safely for publication and rollback
+ok 112 - cross-device moves copy and unlink safely for publication and rollback
+ ---
+ duration_ms: 2.162709
+ type: 'test'
+ ...
+# file:///private/tmp/cssearth-pr390/site/prepared-context-objects.mts:2
+# export const CONTEXT_OBJECT_DESCRIPTORS = import.meta.glob(['../src/objects/betelgeuse-shell/object.json', '../src/objects/galaxy-clusters/object.json', '../src/objects/hd-181327-disc/object.json', '../src/objects/heliosphere/object.json', '../src/objects/helix/object.json', '../src/objects/lmc/object.json', '../src/objects/local-group/object.json', '../src/objects/m1/object.json', '../src/objects/m2-9/object.json', '../src/objects/m31/object.json', '../src/objects/m33/object.json', '../src/objects/m42/object.json', '../src/objects/m45/object.json', '../src/objects/m8/object.json', '../src/objects/milky-way/object.json', '../src/objects/nearby-universe/object.json', '../src/objects/omega-centauri/object.json', '../src/objects/smc/object.json', '../src/objects/stellar-neighbourhood/object.json'], { eager: true, import: 'default' });
+# ^
+# TypeError: (intermediate value).glob is not a function
+# at file:///private/tmp/cssearth-pr390/site/prepared-context-objects.mts:2:80
+# at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
+# at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
+# at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
+# Node.js v22.23.2
+# Subtest: site/test/navigation-router.test.mts
+not ok 34 - site/test/navigation-router.test.mts
+ ---
+ duration_ms: 280.217
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/navigation-router.test.mts:1:1'
+ failureType: 'testCodeFailure'
+ exitCode: 1
+ signal: ~
+ error: 'test failed'
+ code: 'ERR_TEST_FAILURE'
+ ...
+# Subtest: navigation timing records phases once and bounds retained entries without clearing unrelated timing
+ok 114 - navigation timing records phases once and bounds retained entries without clearing unrelated timing
+ ---
+ duration_ms: 0.930792
+ type: 'test'
+ ...
+# Subtest: a settled navigation records no later phase
+ok 115 - a settled navigation records no later phase
+ ---
+ duration_ms: 0.200708
+ type: 'test'
+ ...
+# Subtest: timing is optional in non-browser hosts
+ok 116 - timing is optional in non-browser hosts
+ ---
+ duration_ms: 0.088667
+ type: 'test'
+ ...
+# Subtest: deferred navigation materializes only the selected path from its verified payload
+ok 117 - deferred navigation materializes only the selected path from its verified payload
+ ---
+ duration_ms: 18.215041
+ type: 'test'
+ ...
+# Subtest: bundled navigation reads object packages and prepared context from the repository root
+ok 118 - bundled navigation reads object packages and prepared context from the repository root
+ ---
+ duration_ms: 65.219833
+ type: 'test'
+ ...
+# Subtest: rendered nebulae contribute common names and catalogue aliases to search
+ok 119 - rendered nebulae contribute common names and catalogue aliases to search
+ ---
+ duration_ms: 1.972083
+ type: 'test'
+ ...
+# Subtest: every actual profile reads its recorded coordinates from the shared camera endpoint
+not ok 120 - every actual profile reads its recorded coordinates from the shared camera endpoint
+ ---
+ duration_ms: 760.586041
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/object-browser-profile.test.mts:19:1'
+ failureType: 'testCodeFailure'
+ error: 'ryugu: browser profile must declare retained interaction evidence'
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected: true
+ actual: false
+ operator: '=='
+ stack: |-
+ validatePlanetBrowserProfile (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.mts:51:10)
+ loadPlanetBrowserProfile (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.mts:19:10)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/object-browser-profile.test.mts:21:21)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3)
+ ...
+# Subtest: coordinate field selection is immutable reporting data, with no private camera implementation
+ok 121 - coordinate field selection is immutable reporting data, with no private camera implementation
+ ---
+ duration_ms: 0.723209
+ type: 'test'
+ ...
+# Subtest: restoring a complete camera observation preserves yaw and the pitch alias
+ok 122 - restoring a complete camera observation preserves yaw and the pitch alias
+ ---
+ duration_ms: 1.17075
+ type: 'test'
+ ...
+# Subtest: camera reporting rejects missing, repeated and unsupported coordinates
+ok 123 - camera reporting rejects missing, repeated and unsupported coordinates
+ ---
+ duration_ms: 0.300541
+ type: 'test'
+ ...
+# Subtest: an already ready object does not depend on a redundant browser waiter
+not ok 124 - an already ready object does not depend on a redundant browser waiter
+ ---
+ duration_ms: 204.895083
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/object-browser-profile.test.mts:53:1'
+ failureType: 'testCodeFailure'
+ error: 'ryugu: browser profile must declare retained interaction evidence'
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected: true
+ actual: false
+ operator: '=='
+ stack: |-
+ validatePlanetBrowserProfile (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.mts:51:10)
+ loadPlanetBrowserProfile (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.mts:19:10)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/object-browser-profile.test.mts:55:21)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: a missing or loading object still requires successful readiness before returning
+ok 125 - a missing or loading object still requires successful readiness before returning
+ ---
+ duration_ms: 2.240125
+ type: 'test'
+ ...
+# Subtest: the actual Saturn profile observes one exclusive lens and rejects the former dual selection
+ok 126 - the actual Saturn profile observes one exclusive lens and rejects the former dual selection
+ ---
+ duration_ms: 5.110875
+ type: 'test'
+ ...
+# Subtest: camera observations reject missing or non-finite required coordinates
+ok 127 - camera observations reject missing or non-finite required coordinates
+ ---
+ duration_ms: 1.073875
+ type: 'test'
+ ...
+# Subtest: all four type searches and scene highlights contain the same eligible registered objects
+ok 128 - all four type searches and scene highlights contain the same eligible registered objects
+ ---
+ duration_ms: 3.374667
+ type: 'test'
+ ...
+# Subtest: an authored orientation reference passes through discovery as a validated tier
+ok 129 - an authored orientation reference passes through discovery as a validated tier
+ ---
+ duration_ms: 0.429833
+ type: 'test'
+ ...
+# Subtest: only a prepared observation promotes an illustration, without a second flag change
+ok 130 - only a prepared observation promotes an illustration, without a second flag change
+ ---
+ duration_ms: 0.294916
+ type: 'test'
+ ...
+# Subtest: model textures, shape-derived elevation and featured overrides cannot promote an illustration
+ok 131 - model textures, shape-derived elevation and featured overrides cannot promote an illustration
+ ---
+ duration_ms: 0.110959
+ type: 'test'
+ ...
+# Subtest: partial photographic coverage still counts; source and lens counts do not
+ok 132 - partial photographic coverage still counts; source and lens counts do not
+ ---
+ duration_ms: 0.097125
+ type: 'test'
+ ...
+# Subtest: default discovery admits photographed asteroids and hides illustrations across classifications
+ok 133 - default discovery admits photographed asteroids and hides illustrations across classifications
+ ---
+ duration_ms: 0.575416
+ type: 'test'
+ ...
+# Subtest: a star with only its shape stays off the map, whatever the settings
+ok 134 - a star with only its shape stays off the map, whatever the settings
+ ---
+ duration_ms: 10.183333
+ type: 'test'
+ ...
+# Subtest: a shape-only star that a body with imagery orbits stays on the map with its planet
+ok 135 - a shape-only star that a body with imagery orbits stays on the map with its planet
+ ---
+ duration_ms: 0.29675
+ type: 'test'
+ ...
+# Subtest: a star whose colour comes from its own measurements stays on the map
+ok 136 - a star whose colour comes from its own measurements stays on the map
+ ---
+ duration_ms: 0.427167
+ type: 'test'
+ ...
+# Subtest: category browsing and asteroid settings cannot bypass Illustration models
+not ok 137 - category browsing and asteroid settings cannot bypass Illustration models
+ ---
+ duration_ms: 0.714209
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/object-discovery.test.mts:136:1'
+ failureType: 'testCodeFailure'
+ error: |-
+ pallas
+
+ true !== false
+
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected: false
+ actual: true
+ operator: 'strictEqual'
+ stack: |-
+ TestContext. (file:///private/tmp/cssearth-pr390/site/test/object-discovery.test.mts:143:12)
+ Test.runInAsyncScope (node:async_hooks:214:14)
+ Test.run (node:internal/test_runner/test:1047:25)
+ Test.processPendingSubtests (node:internal/test_runner/test:744:18)
+ Test.postRun (node:internal/test_runner/test:1173:19)
+ Test.run (node:internal/test_runner/test:1101:12)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: photographic arrivals use the prepared package camera, excluding modeled and unexposed datasets
+not ok 138 - photographic arrivals use the prepared package camera, excluding modeled and unexposed datasets
+ ---
+ duration_ms: 31.558708
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/object-discovery.test.mts:150:1'
+ failureType: 'testCodeFailure'
+ error: |-
+ The expression evaluated to a falsy value:
+
+ assert.ok(Math.abs(rotation[index] - value) < 1e-12)
+
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected: true
+ actual: false
+ operator: '=='
+ stack: |-
+ file:///private/tmp/cssearth-pr390/site/test/object-discovery.test.mts:159:57
+ Array.forEach ()
+ TestContext. (file:///private/tmp/cssearth-pr390/site/test/object-discovery.test.mts:159:24)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: a package that was never prepared is discoverable as shape only instead of failing the catalogue
+ok 139 - a package that was never prepared is discoverable as shape only instead of failing the catalogue
+ ---
+ duration_ms: 12.366416
+ type: 'test'
+ ...
+# Subtest: search contains every object, including the Sun; only planets enter the scale
+ok 140 - search contains every object, including the Sun; only planets enter the scale
+ ---
+ duration_ms: 1.030042
+ type: 'test'
+ ...
+# Subtest: prepared marker atlases and presentation follow packages
+ok 141 - prepared marker atlases and presentation follow packages
+ ---
+ duration_ms: 887.737834
+ type: 'test'
+ ...
+# Subtest: an unknown object loads its own marker; missing packages fail without fallback
+ok 142 - an unknown object loads its own marker; missing packages fail without fallback
+ ---
+ duration_ms: 733.505166
+ type: 'test'
+ ...
+# Subtest: scale overrides must form a complete presentation after inheritance
+ok 143 - scale overrides must form a complete presentation after inheritance
+ ---
+ duration_ms: 0.488708
+ type: 'test'
+ ...
+# Subtest: mercury material failure through shared wheel retires once and disables retained callbacks
+ok 144 - mercury material failure through shared wheel retires once and disables retained callbacks
+ ---
+ duration_ms: 77.254583
+ type: 'test'
+ ...
+# Subtest: mercury material failure through shared drag retires once and disables retained callbacks
+ok 145 - mercury material failure through shared drag retires once and disables retained callbacks
+ ---
+ duration_ms: 26.878625
+ type: 'test'
+ ...
+# Subtest: mercury material failure through shared resize retires once and disables retained callbacks
+ok 146 - mercury material failure through shared resize retires once and disables retained callbacks
+ ---
+ duration_ms: 225.772708
+ type: 'test'
+ ...
+# Subtest: mercury material failure through shared invalidate retires once and disables retained callbacks
+ok 147 - mercury material failure through shared invalidate retires once and disables retained callbacks
+ ---
+ duration_ms: 55.430958
+ type: 'test'
+ ...
+# Subtest: mercury material failure through shared refresh retires once and disables retained callbacks
+ok 148 - mercury material failure through shared refresh retires once and disables retained callbacks
+ ---
+ duration_ms: 31.060709
+ type: 'test'
+ ...
+# Subtest: mercury material failure through shared setState retires once and disables retained callbacks
+ok 149 - mercury material failure through shared setState retires once and disables retained callbacks
+ ---
+ duration_ms: 42.2135
+ type: 'test'
+ ...
+# Subtest: mercury material failure through shared media-change retires once and disables retained callbacks
+ok 150 - mercury material failure through shared media-change retires once and disables retained callbacks
+ ---
+ duration_ms: 32.738042
+ type: 'test'
+ ...
+# Subtest: mercury initial material failure stops construction before live error reporting
+ok 151 - mercury initial material failure stops construction before live error reporting
+ ---
+ duration_ms: 45.892125
+ type: 'test'
+ ...
+# Subtest: venus material failure through shared wheel retires once and disables retained callbacks
+ok 152 - venus material failure through shared wheel retires once and disables retained callbacks
+ ---
+ duration_ms: 19.854583
+ type: 'test'
+ ...
+# Subtest: venus material failure through shared drag retires once and disables retained callbacks
+ok 153 - venus material failure through shared drag retires once and disables retained callbacks
+ ---
+ duration_ms: 31.423916
+ type: 'test'
+ ...
+# Subtest: venus material failure through shared resize retires once and disables retained callbacks
+ok 154 - venus material failure through shared resize retires once and disables retained callbacks
+ ---
+ duration_ms: 20.789125
+ type: 'test'
+ ...
+# Subtest: venus material failure through shared invalidate retires once and disables retained callbacks
+ok 155 - venus material failure through shared invalidate retires once and disables retained callbacks
+ ---
+ duration_ms: 10.31225
+ type: 'test'
+ ...
+# Subtest: venus material failure through shared refresh retires once and disables retained callbacks
+ok 156 - venus material failure through shared refresh retires once and disables retained callbacks
+ ---
+ duration_ms: 11.345958
+ type: 'test'
+ ...
+# Subtest: venus material failure through shared setState retires once and disables retained callbacks
+ok 157 - venus material failure through shared setState retires once and disables retained callbacks
+ ---
+ duration_ms: 21.690167
+ type: 'test'
+ ...
+# Subtest: venus material failure through shared media-change retires once and disables retained callbacks
+ok 158 - venus material failure through shared media-change retires once and disables retained callbacks
+ ---
+ duration_ms: 49.692291
+ type: 'test'
+ ...
+# Subtest: venus initial material failure stops construction before live error reporting
+ok 159 - venus initial material failure stops construction before live error reporting
+ ---
+ duration_ms: 13.313167
+ type: 'test'
+ ...
+# Subtest: mars material failure through shared wheel retires once and disables retained callbacks
+ok 160 - mars material failure through shared wheel retires once and disables retained callbacks
+ ---
+ duration_ms: 13.28
+ type: 'test'
+ ...
+# Subtest: mars material failure through shared drag retires once and disables retained callbacks
+ok 161 - mars material failure through shared drag retires once and disables retained callbacks
+ ---
+ duration_ms: 10.726875
+ type: 'test'
+ ...
+# Subtest: mars material failure through shared resize retires once and disables retained callbacks
+ok 162 - mars material failure through shared resize retires once and disables retained callbacks
+ ---
+ duration_ms: 12.484625
+ type: 'test'
+ ...
+# Subtest: mars material failure through shared invalidate retires once and disables retained callbacks
+ok 163 - mars material failure through shared invalidate retires once and disables retained callbacks
+ ---
+ duration_ms: 9.784708
+ type: 'test'
+ ...
+# Subtest: mars material failure through shared refresh retires once and disables retained callbacks
+ok 164 - mars material failure through shared refresh retires once and disables retained callbacks
+ ---
+ duration_ms: 9.322583
+ type: 'test'
+ ...
+# Subtest: mars material failure through shared setState retires once and disables retained callbacks
+ok 165 - mars material failure through shared setState retires once and disables retained callbacks
+ ---
+ duration_ms: 19.617291
+ type: 'test'
+ ...
+# Subtest: mars material failure through shared media-change retires once and disables retained callbacks
+ok 166 - mars material failure through shared media-change retires once and disables retained callbacks
+ ---
+ duration_ms: 30.514292
+ type: 'test'
+ ...
+# Subtest: mars initial material failure stops construction before live error reporting
+ok 167 - mars initial material failure stops construction before live error reporting
+ ---
+ duration_ms: 25.730834
+ type: 'test'
+ ...
+# Subtest: accepts a complete non-NASA package and still rejects corrupt or undeclared bytes
+ok 168 - accepts a complete non-NASA package and still rejects corrupt or undeclared bytes
+ ---
+ duration_ms: 61.114791
+ type: 'test'
+ ...
+# Subtest: derives the complete owned file contract from planet identity
+ok 169 - derives the complete owned file contract from planet identity
+ ---
+ duration_ms: 0.265791
+ type: 'test'
+ ...
+# Subtest: requires every registered object package file
+ok 170 - requires every registered object package file
+ ---
+ duration_ms: 1117.32
+ type: 'test'
+ ...
+# Subtest: validates local editorial identity and provenance
+ok 171 - validates local editorial identity and provenance
+ ---
+ duration_ms: 1.219333
+ type: 'test'
+ ...
+# Subtest: validates runtime asset manifest entries
+ok 172 - validates runtime asset manifest entries
+ ---
+ duration_ms: 0.1965
+ type: 'test'
+ ...
+# Subtest: page metadata stays hash-bound to its scene without needing scene bytes during page emission
+ok 173 - page metadata stays hash-bound to its scene without needing scene bytes during page emission
+ ---
+ duration_ms: 24.432875
+ type: 'test'
+ ...
+# Subtest: all registry objects own ordered CSS and scene-bound page metadata
+ok 174 - all registry objects own ordered CSS and scene-bound page metadata
+ ---
+ duration_ms: 3841.606834
+ type: 'test'
+ ...
+# Subtest: generic routes derive membership from SCENE_OBJECTS and never import scene transports
+ok 175 - generic routes derive membership from SCENE_OBJECTS and never import scene transports
+ ---
+ duration_ms: 2.19175
+ type: 'test'
+ ...
+# Subtest: defines one generic renderable-object contract
+ok 176 - defines one generic renderable-object contract
+ ---
+ duration_ms: 0.676583
+ type: 'test'
+ ...
+# Subtest: world-frame capability is validated and copied at the registry boundary
+ok 177 - world-frame capability is validated and copied at the registry boundary
+ ---
+ duration_ms: 4.593625
+ type: 'test'
+ ...
+# Subtest: rejects invalid object definitions and renderer-specific fields
+ok 178 - rejects invalid object definitions and renderer-specific fields
+ ---
+ duration_ms: 0.45975
+ type: 'test'
+ ...
+# Subtest: keeps one open-ended object registry with unique ids and routes
+ok 179 - keeps one open-ended object registry with unique ids and routes
+ ---
+ duration_ms: 1.069333
+ type: 'test'
+ ...
+# Subtest: derives deterministic test discovery from an authored object id
+not ok 180 - derives deterministic test discovery from an authored object id
+ ---
+ duration_ms: 9.428791
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/object-schema.test.mts:131:1'
+ failureType: 'testCodeFailure'
+ error: |-
+ Expected values to be strictly equal:
+ + actual - expected
+
+ + '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/cssearth-discovery-tcOXq1/tests/objects/unit'
+ - '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/cssearth-discovery-tcOXq1/tests/objects/unit/fixture'
+
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected: '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/cssearth-discovery-tcOXq1/tests/objects/unit/fixture'
+ actual: '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/cssearth-discovery-tcOXq1/tests/objects/unit'
+ operator: 'strictEqual'
+ stack: |-
+ readDirectory (file:///private/tmp/cssearth-pr390/site/test/object-schema.test.mts:138:16)
+ discoverPlanetTests (file:///private/tmp/cssearth-pr390/tools/run-implemented-planets.mts:47:26)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/object-schema.test.mts:135:5)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: derives shared preparation and external browser scripts from the descriptor
+ok 181 - derives shared preparation and external browser scripts from the descriptor
+ ---
+ duration_ms: 14.128208
+ type: 'test'
+ ...
+# Subtest: planetary systems follow prepared orbit chains to their stars
+ok 182 - planetary systems follow prepared orbit chains to their stars
+ ---
+ duration_ms: 1.439416
+ type: 'test'
+ ...
+# Subtest: a system's exit distance scales the Sun's 100 AU by the prepared framing radius
+ok 183 - a system's exit distance scales the Sun's 100 AU by the prepared framing radius
+ ---
+ duration_ms: 0.073083
+ type: 'test'
+ ...
+# Subtest: every member names its star’s system
+ok 184 - every member names its star’s system
+ ---
+ duration_ms: 0.535292
+ type: 'test'
+ ...
+# Subtest: publishable text covers every dataset, stays within budget and cites catalogued sources
+ok 185 - publishable text covers every dataset, stays within budget and cites catalogued sources
+ ---
+ duration_ms: 1.666333
+ type: 'test'
+ ...
+# Subtest: filler, display words and repeated titles are warnings for a reviewer, not errors
+ok 186 - filler, display words and repeated titles are warnings for a reviewer, not errors
+ ---
+ duration_ms: 3.259875
+ type: 'test'
+ ...
+# Subtest: text shown together may not repeat a sentence or a six-word phrase, including identical blocks
+ok 187 - text shown together may not repeat a sentence or a six-word phrase, including identical blocks
+ ---
+ duration_ms: 1.11475
+ type: 'test'
+ ...
+# Subtest: published text keeps the authored blocks and names the input it was checked from
+ok 188 - published text keeps the authored blocks and names the input it was checked from
+ ---
+ duration_ms: 0.233083
+ type: 'test'
+ ...
+# Subtest: body cards retain overview through small boundary reversals for bodies with and without moons
+ok 189 - body cards retain overview through small boundary reversals for bodies with and without moons
+ ---
+ duration_ms: 0.499917
+ type: 'test'
+ ...
+# Subtest: body cards switch at the shared camera detail threshold, independent of camera aim
+ok 190 - body cards switch at the shared camera detail threshold, independent of camera aim
+ ---
+ duration_ms: 0.520875
+ type: 'test'
+ ...
+# Subtest: overview follows the prepared galaxy fade and restores correctly at maximum zoom
+ok 191 - overview follows the prepared galaxy fade and restores correctly at maximum zoom
+ ---
+ duration_ms: 0.082583
+ type: 'test'
+ ...
+# Subtest: galactic distance is measured from the Sun, independent of selected body and surface radius
+ok 192 - galactic distance is measured from the Sun, independent of selected body and surface radius
+ ---
+ duration_ms: 0.07175
+ type: 'test'
+ ...
+# Subtest: prepared focus distance follows its catalogue position independently of the selected detail and overview scope
+ok 193 - prepared focus distance follows its catalogue position independently of the selected detail and overview scope
+ ---
+ duration_ms: 0.218583
+ type: 'test'
+ ...
+# Subtest: extragalactic overview cards follow zoom with hysteresis and preserve distance meaning
+ok 194 - extragalactic overview cards follow zoom with hysteresis and preserve distance meaning
+ ---
+ duration_ms: 0.1005
+ type: 'test'
+ ...
+# Subtest: every body switches to Solar System at 100 AU from the Sun
+ok 195 - every body switches to Solar System at 100 AU from the Sun
+ ---
+ duration_ms: 5.627333
+ type: 'test'
+ ...
+# Subtest: the threshold follows the Sun origin even in a translated world frame
+ok 196 - the threshold follows the Sun origin even in a translated world frame
+ ---
+ duration_ms: 0.483083
+ type: 'test'
+ ...
+# Subtest: another star's planetary system opens its own overview, scaled by the system's prepared size
+ok 197 - another star's planetary system opens its own overview, scaled by the system's prepared size
+ ---
+ duration_ms: 1.20275
+ type: 'test'
+ ...
+# Subtest: a star outside every system keeps its scene until the camera is as far from it as the Sun is
+ok 198 - a star outside every system keeps its scene until the camera is as far from it as the Sun is
+ ---
+ duration_ms: 0.411333
+ type: 'test'
+ ...
+# Subtest: only approaching the Sun opens a card, with separate entry and exit thresholds
+ok 199 - only approaching the Sun opens a card, with separate entry and exit thresholds
+ ---
+ duration_ms: 0.163709
+ type: 'test'
+ ...
+# Subtest: camera sampling settles before changing selection and releases timers and subscriptions
+ok 200 - camera sampling settles before changing selection and releases timers and subscriptions
+ ---
+ duration_ms: 0.261542
+ type: 'test'
+ ...
+# Subtest: continuous outward camera updates cannot postpone the Sun overview flip
+ok 201 - continuous outward camera updates cannot postpone the Sun overview flip
+ ---
+ duration_ms: 0.79
+ type: 'test'
+ ...
+# file:///private/tmp/cssearth-pr390/site/prepared-context-objects.mts:2
+# export const CONTEXT_OBJECT_DESCRIPTORS = import.meta.glob(['../src/objects/betelgeuse-shell/object.json', '../src/objects/galaxy-clusters/object.json', '../src/objects/hd-181327-disc/object.json', '../src/objects/heliosphere/object.json', '../src/objects/helix/object.json', '../src/objects/lmc/object.json', '../src/objects/local-group/object.json', '../src/objects/m1/object.json', '../src/objects/m2-9/object.json', '../src/objects/m31/object.json', '../src/objects/m33/object.json', '../src/objects/m42/object.json', '../src/objects/m45/object.json', '../src/objects/m8/object.json', '../src/objects/milky-way/object.json', '../src/objects/nearby-universe/object.json', '../src/objects/omega-centauri/object.json', '../src/objects/smc/object.json', '../src/objects/stellar-neighbourhood/object.json'], { eager: true, import: 'default' });
+# ^
+# TypeError: (intermediate value).glob is not a function
+# at file:///private/tmp/cssearth-pr390/site/prepared-context-objects.mts:2:80
+# at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
+# at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
+# at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
+# Node.js v22.23.2
+# Subtest: site/test/planet-shell.test.mts
+not ok 50 - site/test/planet-shell.test.mts
+ ---
+ duration_ms: 367.977791
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/planet-shell.test.mts:1:1'
+ failureType: 'testCodeFailure'
+ exitCode: 1
+ signal: ~
+ error: 'test failed'
+ code: 'ERR_TEST_FAILURE'
+ ...
+# Subtest: Node preparation has a closed prepared-data dependency graph
+ok 203 - Node preparation has a closed prepared-data dependency graph
+ ---
+ duration_ms: 200.963875
+ type: 'test'
+ ...
+# Subtest: a saved lens link to an unavailable package still opens its actual catalogue record
+ok 204 - a saved lens link to an unavailable package still opens its actual catalogue record
+ ---
+ duration_ms: 2.056667
+ type: 'test'
+ ...
+# Subtest: a direct focus link without a saved camera frames its target immediately
+ok 205 - a direct focus link without a saved camera frames its target immediately
+ ---
+ duration_ms: 0.31625
+ type: 'test'
+ ...
+# Subtest: a focus link with a saved camera restores selection without reframing
+ok 206 - a focus link with a saved camera restores selection without reframing
+ ---
+ duration_ms: 0.147875
+ type: 'test'
+ ...
+# Subtest: suspension isolates camera restore publications from incoming focus history and cancels an older selection flight
+ok 207 - suspension isolates camera restore publications from incoming focus history and cancels an older selection flight
+ ---
+ duration_ms: 0.390833
+ type: 'test'
+ ...
+# Subtest: an invalid incoming catalogue ID remains available for diagnosis after restoration publications
+ok 208 - an invalid incoming catalogue ID remains available for diagnosis after restoration publications
+ ---
+ duration_ms: 0.206291
+ type: 'test'
+ ...
+# Subtest: an authored framing radius takes precedence over an oversized transparent image frame
+ok 209 - an authored framing radius takes precedence over an oversized transparent image frame
+ ---
+ duration_ms: 0.351834
+ type: 'test'
+ ...
+# Subtest: a cluster focus uses its prepared aperture framing and source without pretending it has galaxy membership
+ok 210 - a cluster focus uses its prepared aperture framing and source without pretending it has galaxy membership
+ ---
+ duration_ms: 0.209667
+ type: 'test'
+ ...
+# Subtest: volume focus uses its authored framing radius before transparent bounds and retains an explicit catalogue override
+ok 211 - volume focus uses its authored framing radius before transparent bounds and retains an explicit catalogue override
+ ---
+ duration_ms: 0.444042
+ type: 'test'
+ ...
+# Subtest: focused lens selection and star visibility follow applied runtime state while URL restore keeps the same camera owner
+ok 212 - focused lens selection and star visibility follow applied runtime state while URL restore keeps the same camera owner
+ ---
+ duration_ms: 0.360667
+ type: 'test'
+ ...
+# Subtest: invalid focused lenses retain their diagnostic URL and never apply an arbitrary bank
+ok 213 - invalid focused lenses retain their diagnostic URL and never apply an arbitrary bank
+ ---
+ duration_ms: 0.367875
+ type: 'test'
+ ...
+# Subtest: a lens query without a focus is removed and focused bank subscriptions are released on destruction
+ok 214 - a lens query without a focus is removed and focused bank subscriptions are released on destruction
+ ---
+ duration_ms: 0.158458
+ type: 'test'
+ ...
+# Subtest: an image-layer focus exposes its single optical dataset without volume-only actions
+ok 215 - an image-layer focus exposes its single optical dataset without volume-only actions
+ ---
+ duration_ms: 0.090708
+ type: 'test'
+ ...
+# Subtest: cold native focus waits for the real bank framing before choosing a destination
+ok 216 - cold native focus waits for the real bank framing before choosing a destination
+ ---
+ duration_ms: 0.203334
+ type: 'test'
+ ...
+# Subtest: ordinary cold selection waits for the same authored framing and cancellation prevents stale focus
+ok 217 - ordinary cold selection waits for the same authored framing and cancellation prevents stale focus
+ ---
+ duration_ms: 4.117209
+ type: 'test'
+ ...
+# Subtest: cold saved camera keeps its pose while waiting for an explicitly selected lens
+ok 218 - cold saved camera keeps its pose while waiting for an explicitly selected lens
+ ---
+ duration_ms: 0.30025
+ type: 'test'
+ ...
+# Subtest: a failed bank load reports its actual error and never falls back to an inaccurate destination
+ok 219 - a failed bank load reports its actual error and never falls back to an inaccurate destination
+ ---
+ duration_ms: 0.110458
+ type: 'test'
+ ...
+# Subtest: a later saved restore supersedes a pending native focus without a delayed flight
+ok 220 - a later saved restore supersedes a pending native focus without a delayed flight
+ ---
+ duration_ms: 0.146292
+ type: 'test'
+ ...
+# Subtest: shared context receives the exact authoritative focus on restore, switch and clear
+ok 221 - shared context receives the exact authoritative focus on restore, switch and clear
+ ---
+ duration_ms: 0.212042
+ type: 'test'
+ ...
+# Subtest: reads the validated descriptor embedded in an object page
+ok 222 - reads the validated descriptor embedded in an object page
+ ---
+ duration_ms: 1.058917
+ type: 'test'
+ ...
+# Subtest: publishes the retained document descriptor after navigation
+ok 223 - publishes the retained document descriptor after navigation
+ ---
+ duration_ms: 0.176709
+ type: 'test'
+ ...
+# Subtest: allows descriptor-free unit documents but refuses to publish into one
+ok 224 - allows descriptor-free unit documents but refuses to publish into one
+ ---
+ duration_ms: 0.088
+ type: 'test'
+ ...
+# Subtest: keeps every planet factsheet comparable and concise
+not ok 225 - keeps every planet factsheet comparable and concise
+ ---
+ duration_ms: 1.675584
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/prepared-factsheet-model.test.mts:62:1'
+ failureType: 'testCodeFailure'
+ error: |-
+ mercury core facts
+ + actual - expected
+
+ [
+ 'distance-from-sun',
+ 'radius',
+ 'orbital-period',
+ 'rotation-period',
+ 'axial-tilt',
+ - 'moon-count',
+ - 'ring-system'
+ ]
+
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected:
+ 0: 'distance-from-sun'
+ 1: 'radius'
+ 2: 'orbital-period'
+ 3: 'rotation-period'
+ 4: 'axial-tilt'
+ 5: 'moon-count'
+ 6: 'ring-system'
+ actual:
+ 0: 'distance-from-sun'
+ 1: 'radius'
+ 2: 'orbital-period'
+ 3: 'rotation-period'
+ 4: 'axial-tilt'
+ operator: 'deepStrictEqual'
+ stack: |-
+ TestContext. (file:///private/tmp/cssearth-pr390/site/test/prepared-factsheet-model.test.mts:64:12)
+ Test.runInAsyncScope (node:async_hooks:214:14)
+ Test.run (node:internal/test_runner/test:1047:25)
+ Test.start (node:internal/test_runner/test:944:17)
+ startSubtestAfterBootstrap (node:internal/test_runner/harness:296:17)
+ ...
+# Subtest: uses unique semantic ids without hidden title-only content
+ok 226 - uses unique semantic ids without hidden title-only content
+ ---
+ duration_ms: 0.378958
+ type: 'test'
+ ...
+# Subtest: keeps Pluto concise and removes lens-presentation rows
+ok 227 - keeps Pluto concise and removes lens-presentation rows
+ ---
+ duration_ms: 0.121
+ type: 'test'
+ ...
+# Subtest: keeps object-internal identifiers out of the Moon factsheet
+ok 228 - keeps object-internal identifiers out of the Moon factsheet
+ ---
+ duration_ms: 0.077125
+ type: 'test'
+ ...
+# Subtest: an unavailable volume keeps catalogue facts and a retained explanation, without stale dataset controls
+ok 229 - an unavailable volume keeps catalogue facts and a retained explanation, without stale dataset controls
+ ---
+ duration_ms: 1.450875
+ type: 'test'
+ ...
+# Subtest: prepared focus lenses retain controls and reflect only the applied runtime selection
+ok 230 - prepared focus lenses retain controls and reflect only the applied runtime selection
+ ---
+ duration_ms: 0.918625
+ type: 'test'
+ ...
+# Subtest: departed or unsupported galaxy focus hides its retained lens bank and disables stale actions
+ok 231 - departed or unsupported galaxy focus hides its retained lens bank and disables stale actions
+ ---
+ duration_ms: 0.91725
+ type: 'test'
+ ...
+# Subtest: a nebula focus displays its actual classification and preserves the shared lens controls
+ok 232 - a nebula focus displays its actual classification and preserves the shared lens controls
+ ---
+ duration_ms: 0.227625
+ type: 'test'
+ ...
+# Subtest: a globular-cluster focus keeps its stellar classification and the shared lens controls
+ok 233 - a globular-cluster focus keeps its stellar classification and the shared lens controls
+ ---
+ duration_ms: 0.187791
+ type: 'test'
+ ...
+# Subtest: focus uses shared dataset tabs only when a prepared presentation is available
+ok 234 - focus uses shared dataset tabs only when a prepared presentation is available
+ ---
+ duration_ms: 0.121875
+ type: 'test'
+ ...
+# Subtest: prepares one source-bound 20px SVG family for sidebar sections
+ok 235 - prepares one source-bound 20px SVG family for sidebar sections
+ ---
+ duration_ms: 23.744042
+ type: 'test'
+ ...
+# Subtest: authored lens facts survive preparation and runtime publication without mutating the source
+ok 236 - authored lens facts survive preparation and runtime publication without mutating the source
+ ---
+ duration_ms: 14.720333
+ type: 'test'
+ ...
+# Subtest: lens facts reject malformed rows and duplicate ids before publication
+ok 237 - lens facts reject malformed rows and duplicate ids before publication
+ ---
+ duration_ms: 12.532708
+ type: 'test'
+ ...
+# Subtest: lenses without facts publish none, and no lens carries reader text
+ok 238 - lenses without facts publish none, and no lens carries reader text
+ ---
+ duration_ms: 5.966041
+ type: 'test'
+ ...
+# Subtest: names arbitrary lens IDs without changing source data or assuming a fixed set of concepts
+ok 239 - names arbitrary lens IDs without changing source data or assuming a fixed set of concepts
+ ---
+ duration_ms: 1.388416
+ type: 'test'
+ ...
+# Subtest: prepares a frozen, smoothly sampled scale legend
+ok 240 - prepares a frozen, smoothly sampled scale legend
+ ---
+ duration_ms: 0.681042
+ type: 'test'
+ ...
+# Subtest: prepares source-backed category legends from hex and RGB colors
+ok 241 - prepares source-backed category legends from hex and RGB colors
+ ---
+ duration_ms: 0.454417
+ type: 'test'
+ ...
+# Subtest: rejects invalid scale palettes
+ok 242 - rejects invalid scale palettes
+ ---
+ duration_ms: 0.193208
+ type: 'test'
+ ...
+# Subtest: named categories can omit a redundant description while supplied descriptions stay validated
+ok 243 - named categories can omit a redundant description while supplied descriptions stay validated
+ ---
+ duration_ms: 0.176167
+ type: 'test'
+ ...
+# Subtest: every object forwards its object-owned legend through the shared shell
+not ok 244 - every object forwards its object-owned legend through the shared shell
+ ---
+ duration_ms: 96.342375
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/prepared-lens-legends.test.mts:72:1'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, open '/private/tmp/cssearth-pr390/src/objects/a0952p69/object.json'"
+ code: 'ENOENT'
+ stack: |-
+ async open (node:internal/fs/promises:639:25)
+ async readFile (node:internal/fs/promises:1252:14)
+ async readJson (file:///private/tmp/cssearth-pr390/site/test/load-object-content.mts:29:73)
+ async loadObjectContent (file:///private/tmp/cssearth-pr390/site/test/load-object-content.mts:30:40)
+ async file:///private/tmp/cssearth-pr390/site/test/prepared-lens-legends.test.mts:74:20
+ async Promise.all (index 496)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/prepared-lens-legends.test.mts:73:3)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: every registered scene supplies typed shared panel content and controls
+ok 245 - every registered scene supplies typed shared panel content and controls
+ ---
+ duration_ms: 430.979709
+ type: 'test'
+ ...
+# Subtest: unknown panel values are rejected before they can claim rendered field types
+ok 246 - unknown panel values are rejected before they can claim rendered field types
+ ---
+ duration_ms: 1.618791
+ type: 'test'
+ ...
+# Subtest: generates all shared title assets once from checked source vectors
+ok 247 - generates all shared title assets once from checked source vectors
+ ---
+ duration_ms: 36.698584
+ type: 'test'
+ ...
+# Subtest: keeps planet title rendering facts object-owned and source-bound
+ok 248 - keeps planet title rendering facts object-owned and source-bound
+ ---
+ duration_ms: 3412.110917
+ type: 'test'
+ ...
+# Subtest: normalizes every implemented planet title to the complete Saturn standard
+ok 249 - normalizes every implemented planet title to the complete Saturn standard
+ ---
+ duration_ms: 1113.7985
+ type: 'test'
+ ...
+# Subtest: scene selection and cross-object flight use the prepared photographic face
+ok 250 - scene selection and cross-object flight use the prepared photographic face
+ ---
+ duration_ms: 79.852375
+ type: 'test'
+ ...
+# Subtest: saved views and explicit non-photographic datasets keep their requested direction
+ok 251 - saved views and explicit non-photographic datasets keep their requested direction
+ ---
+ duration_ms: 91.06975
+ type: 'test'
+ ...
+# Subtest: a prepared photographic angle never rotates a system overview or an explicit target
+ok 252 - a prepared photographic angle never rotates a system overview or an explicit target
+ ---
+ duration_ms: 50.269542
+ type: 'test'
+ ...
+# Subtest: a falsy camera publication failure rejects and releases native flight listeners
+ok 253 - a falsy camera publication failure rejects and releases native flight listeners
+ ---
+ duration_ms: 1.298333
+ type: 'test'
+ ...
+# Subtest: a terminal extreme-range pose finishes without a tail of identical publications
+ok 254 - a terminal extreme-range pose finishes without a tail of identical publications
+ ---
+ duration_ms: 59.371125
+ type: 'test'
+ ...
+# Subtest: a long fly-to ends once its remaining approach no longer moves the body on screen
+ok 255 - a long fly-to ends once its remaining approach no longer moves the body on screen
+ ---
+ duration_ms: 30.785541
+ type: 'test'
+ ...
+# Subtest: a short center selection slows into its target instead of stopping at full speed
+ok 256 - a short center selection slows into its target instead of stopping at full speed
+ ---
+ duration_ms: 46.463333
+ type: 'test'
+ ...
+# Subtest: small bodies and planets grow through a readable close-up at both 60 and 120 Hz
+ok 257 - small bodies and planets grow through a readable close-up at both 60 and 120 Hz
+ ---
+ duration_ms: 406.956625
+ type: 'test'
+ ...
+# Subtest: the exact final camera demand finishes before the old scene is handed off
+ok 258 - the exact final camera demand finishes before the old scene is handed off
+ ---
+ duration_ms: 2.650875
+ type: 'test'
+ ...
+# Subtest: an overview starts moving before factory and assets are ready, then holds before target detail
+ok 259 - an overview starts moving before factory and assets are ready, then holds before target detail
+ ---
+ duration_ms: 15.470209
+ type: 'test'
+ ...
+# Subtest: overview preserves the latest drawn camera through slow preparation, without a flight or input interception
+ok 260 - overview preserves the latest drawn camera through slow preparation, without a flight or input interception
+ ---
+ duration_ms: 0.555417
+ type: 'test'
+ ...
+# Subtest: a cancelled preserved-view handoff releases its unmounted resources
+ok 261 - a cancelled preserved-view handoff releases its unmounted resources
+ ---
+ duration_ms: 0.180583
+ type: 'test'
+ ...
+# Subtest: an animated handoff transfers prepared cleanup to the scene and detaches request ownership
+ok 262 - an animated handoff transfers prepared cleanup to the scene and detaches request ownership
+ ---
+ duration_ms: 20.796542
+ type: 'test'
+ ...
+# Subtest: overview handoff uses the requested distant camera instead of flying into the Sun
+ok 263 - overview handoff uses the requested distant camera instead of flying into the Sun
+ ---
+ duration_ms: 11.938625
+ type: 'test'
+ ...
+# Subtest: the same object can recenter at overview distance without resetting to its close-up
+ok 264 - the same object can recenter at overview distance without resetting to its close-up
+ ---
+ duration_ms: 21.898458
+ type: 'test'
+ ...
+# Subtest: ordinary planet selection clears a prepared catalogue pivot for both same-owner focus and detail handoff
+ok 265 - ordinary planet selection clears a prepared catalogue pivot for both same-owner focus and detail handoff
+ ---
+ duration_ms: 0.835375
+ type: 'test'
+ ...
+# Subtest: handoff precedes large destination approach and continues the same numeric flight from the exact drawn checkpoint
+ok 266 - handoff precedes large destination approach and continues the same numeric flight from the exact drawn checkpoint
+ ---
+ duration_ms: 39.254625
+ type: 'test'
+ ...
+# Subtest: slow asset preparation holds only the distant checkpoint and resumes after readiness
+ok 267 - slow asset preparation holds only the distant checkpoint and resumes after readiness
+ ---
+ duration_ms: 21.206542
+ type: 'test'
+ ...
+# Subtest: new selection cancellation stops painting and releases destination preparation
+ok 268 - new selection cancellation stops painting and releases destination preparation
+ ---
+ duration_ms: 0.721875
+ type: 'test'
+ ...
+# Subtest: cancellation between prepared handoff and mount releases resources and all interruption listeners
+ok 269 - cancellation between prepared handoff and mount releases resources and all interruption listeners
+ ---
+ duration_ms: 3.846834
+ type: 'test'
+ ...
+# Subtest: input between handoff and mount keeps the prepared lease for the mount to claim
+ok 270 - input between handoff and mount keeps the prepared lease for the mount to claim
+ ---
+ duration_ms: 3.998792
+ type: 'test'
+ ...
+# Subtest: an already cancelled request cannot start a frame or retain input listeners
+ok 271 - an already cancelled request cannot start a frame or retain input listeners
+ ---
+ duration_ms: 0.463625
+ type: 'test'
+ ...
+# Subtest: input during a delayed preparation interrupts at the last drawn checkpoint and releases late resources
+ok 272 - input during a delayed preparation interrupts at the last drawn checkpoint and releases late resources
+ ---
+ duration_ms: 15.029792
+ type: 'test'
+ ...
+# Subtest: input after the early handoff hurries between bodies, then preserves the drawn approach pose
+ok 273 - input after the early handoff hurries between bodies, then preserves the drawn approach pose
+ ---
+ duration_ms: 14.320542
+ type: 'test'
+ ...
+# Subtest: reduced motion waits for preparation, then mounts the exact arrival without enlarging a source proxy
+ok 274 - reduced motion waits for preparation, then mounts the exact arrival without enlarging a source proxy
+ ---
+ duration_ms: 0.299209
+ type: 'test'
+ ...
+# Subtest: saved camera endpoints survive the early owner handoff unchanged
+ok 275 - saved camera endpoints survive the early owner handoff unchanged
+ ---
+ duration_ms: 59.623208
+ type: 'test'
+ ...
+# Subtest: refocusing the selected object paints one existing owner without reloading its prepared bank
+ok 276 - refocusing the selected object paints one existing owner without reloading its prepared bank
+ ---
+ duration_ms: 11.392583
+ type: 'test'
+ ...
+# Subtest: the application keeps flying while destination groups activate, then transfers the live pose without a reset
+ok 277 - the application keeps flying while destination groups activate, then transfers the live pose without a reset
+ ---
+ duration_ms: 19.780291
+ type: 'test'
+ ...
+# Subtest: cancellation during connected activation stops the application flight and releases its resources
+ok 278 - cancellation during connected activation stops the application flight and releases its resources
+ ---
+ duration_ms: 2.427541
+ type: 'test'
+ ...
+# Subtest: an activating detail waits for its world publication and interruption preserves the last acknowledged pose
+ok 279 - an activating detail waits for its world publication and interruption preserves the last acknowledged pose
+ ---
+ duration_ms: 8.232667
+ type: 'test'
+ ...
+# Subtest: real input interrupts a same-object focus at the last painted camera
+ok 280 - real input interrupts a same-object focus at the last painted camera
+ ---
+ duration_ms: 0.280875
+ type: 'test'
+ ...
+# Subtest: replacement departure uses the retained world while its previous detail owner is retired
+ok 281 - replacement departure uses the retained world while its previous detail owner is retired
+ ---
+ duration_ms: 15.573542
+ type: 'test'
+ ...
+# Subtest: the current body accepts wide-view centering only while below its prepared detail threshold
+ok 282 - the current body accepts wide-view centering only while below its prepared detail threshold
+ ---
+ duration_ms: 0.2515
+ type: 'test'
+ ...
+# Subtest: centering changes the focus at the same range and orientation, then focus zooms in
+ok 283 - centering changes the focus at the same range and orientation, then focus zooms in
+ ---
+ duration_ms: 73.334833
+ type: 'test'
+ ...
+# Subtest: a wheel during a flight hurries the arrival instead of stopping it
+ok 284 - a wheel during a flight hurries the arrival instead of stopping it
+ ---
+ duration_ms: 24.544334
+ type: 'test'
+ ...
+# Subtest: a wheel, click or key between bodies hurries a prepared navigation to the destination instead of abandoning it
+ok 285 - a wheel, click or key between bodies hurries a prepared navigation to the destination instead of abandoning it
+ ---
+ duration_ms: 100.570958
+ type: 'test'
+ ...
+# Subtest: production runtime removes development observations from the executable closure
+ok 286 - production runtime removes development observations from the executable closure
+ ---
+ duration_ms: 1724.358292
+ type: 'test'
+ ...
+# node:internal/modules/run_main:123
+# triggerUncaughtException(
+# ^
+# AssertionError [ERR_ASSERTION]: jupiter: seam parameters must generate the actual leaves
+# + actual - expected
+# ... Skipped lines
+# {
+# leaves: [
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 0,
+# ...
+# ],
+# - worldFrame: {
+# - bodyRadiusM: 71492000,
+# - epochJdTt: 2461286.5,
+# - metersPerUnit: 282577.07509881427,
+# - orbitUpReference: [
+# - 0,
+# - -0.39777715593191376,
+# - 0.9174820620691818
+# - ],
+# - originM: [
+# - -497567591648.3631,
+# - 562510993077.0295,
+# - 253219020779.19708
+# - ],
+# - presentationToReference: [
+# - 0.8226442516224385,
+# - 0.5683180715506297,
+# - 0.016462224076079956,
+# - 0.5079295291100077,
+# - -0.7216050113418623,
+# - -0.47041875075765904,
+# - 0.25546825386071725,
+# - -0.39534893088926354,
+# - 0.8822897449897467
+# - ],
+# - referenceFrame: 'sun-icrf'
+# - }
+# }
+# at file:///private/tmp/cssearth-pr390/site/test/projective-surface-seam-contract.test.mts:46:12 {
+# generatedMessage: false,
+# code: 'ERR_ASSERTION',
+# actual: {
+# leaves: [
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 0,
+# tag: 's',
+# style: 'transform:matrix3d(12.593394,-1.240342,0,0,0.014782,0.15012,281.621272,-0.025628,0.019205,0.194991,-0.980617,0,-8.377689,1980.5914,-10593.282539,1);width:32px;height:11.3778px;background-position:-8px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.1483485,-0.3100855,0,0,-0.04998035342300001,12.7271790998,2.534156772626986,0,0.019205,0.194991,-0.980617,0,-8.377689,1980.5914,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 1,
+# tag: 's',
+# style: 'transform:matrix3d(12.109438,-3.673358,0,0,0.043784,0.144348,281.621272,-0.025628,0.056877,0.187498,-0.980617,0,378.1775,1944.169297,-10593.282539,1);width:32px;height:11.3778px;background-position:-40px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.0273595,-0.9183395,0,0,2.4339292425000005,12.492379685879,2.534156772626986,0,0.056877,0.187498,-0.980617,0,378.1775,1944.169297,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 2,
+# tag: 's',
+# style: 'transform:matrix3d(11.160122,-5.96521,0,0,0.071106,0.133032,281.621272,-0.025628,0.092363,0.172799,-0.980617,0,750.199539,1833.033859,-10593.282539,1);width:32px;height:11.3778px;background-position:-72px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.7900305,-1.4913025,0,0,4.824304946373,11.777505934613,2.534156772626986,0,0.092363,0.172799,-0.980617,0,750.199539,1833.033859,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 3,
+# tag: 's',
+# style: 'transform:matrix3d(9.781928,-8.02782,0,0,0.095692,0.116606,281.621272,-0.025628,0.1243,0.15146,-0.980617,0,1093.391831,1651.455958,-10593.282539,1);width:32px;height:11.3778px;background-position:-104px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.445482,-2.006955,0,0,7.029284461216999,10.610029822906,2.534156772626986,0,0.1243,0.15146,-0.980617,0,1093.391831,1651.455958,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 4,
+# tag: 's',
+# style: 'transform:matrix3d(8.02782,-9.781928,0,0,0.116606,0.095692,281.621272,-0.025628,0.15146,0.1243,-0.980617,0,1394.565689,1406.41353,-10593.282539,1);width:32px;height:11.3778px;background-position:-136px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.006955,-2.445482,0,0,8.964133869423,9.034814486710001,2.534156772626986,0,0.15146,0.1243,-0.980617,0,1394.565689,1406.41353,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 5,
+# tag: 's',
+# style: 'transform:matrix3d(5.96521,-11.160122,0,0,0.133032,0.071106,281.621272,-0.025628,0.172799,0.092363,-0.980617,0,1642.147169,1107.323419,-10593.282539,1);width:32px;height:11.3778px;background-position:-168px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '1.4913025,-2.7900305,0,0,10.554494911783001,7.112397645533001,2.534156772626986,0,0.172799,0.092363,-0.980617,0,1642.147169,1107.323419,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 6,
+# tag: 's',
+# style: 'transform:matrix3d(3.673358,-12.109438,0,0,0.144348,0.043784,281.621272,-0.025628,0.187498,0.056877,-0.980617,0,1826.621854,765.679491,-10593.282539,1);width:32px;height:11.3778px;background-position:-200px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '0.9183395,-3.0273595,0,0,11.739253218578002,4.916654498837,2.534156772626986,0,0.187498,0.056877,-0.980617,0,1826.621854,765.679491,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 7,
+# tag: 's',
+# style: 'transform:matrix3d(1.240342,-12.593394,0,0,0.15012,0.014782,281.621272,-0.025628,0.194991,0.019205,-0.980617,0,1940.900485,394.610928,-10593.282539,1);width:32px;height:11.3778px;background-position:-232px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '0.3100855,-3.1483485,0,0,12.472879407395,2.531967715696,2.534156772626986,0,0.194991,0.019205,-0.980617,0,1940.900485,394.610928,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 8,
+# tag: 's',
+# style: 'transform:matrix3d(-1.240342,-12.593394,0,0,0.15012,-0.014782,281.621272,-0.025628,0.194991,-0.019205,-0.980617,0,1980.5914,8.377689,-10593.282539,1);width:32px;height:11.3778px;background-position:-264px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-0.3100855,-3.1483485,0,0,12.7271790998,0.04998035342300001,2.534156772626986,0,0.194991,-0.019205,-0.980617,0,1980.5914,8.377689,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 9,
+# tag: 's',
+# style: 'transform:matrix3d(-3.673358,-12.109438,0,0,0.144348,-0.043784,281.621272,-0.025628,0.187498,-0.056877,-0.980617,0,1944.169297,-378.1775,-10593.282539,1);width:32px;height:11.3778px;background-position:-296px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-0.9183395,-3.0273595,0,0,12.492379685879,-2.4339292425000005,2.534156772626986,0,0.187498,-0.056877,-0.980617,0,1944.169297,-378.1775,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 10,
+# tag: 's',
+# style: 'transform:matrix3d(-5.96521,-11.160122,0,0,0.133032,-0.071106,281.621272,-0.025628,0.172799,-0.092363,-0.980617,0,1833.033859,-750.199539,-10593.282539,1);width:32px;height:11.3778px;background-position:-328px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-1.4913025,-2.7900305,0,0,11.777505934613,-4.824304946373,2.534156772626986,0,0.172799,-0.092363,-0.980617,0,1833.033859,-750.199539,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 11,
+# tag: 's',
+# style: 'transform:matrix3d(-8.02782,-9.781928,0,0,0.116606,-0.095692,281.621272,-0.025628,0.15146,-0.1243,-0.980617,0,1651.455958,-1093.391831,-10593.282539,1);width:32px;height:11.3778px;background-position:-360px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.006955,-2.445482,0,0,10.610029822906,-7.029284461216999,2.534156772626986,0,0.15146,-0.1243,-0.980617,0,1651.455958,-1093.391831,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 12,
+# tag: 's',
+# style: 'transform:matrix3d(-9.781928,-8.02782,0,0,0.095692,-0.116606,281.621272,-0.025628,0.1243,-0.15146,-0.980617,0,1406.41353,-1394.565689,-10593.282539,1);width:32px;height:11.3778px;background-position:-392px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.445482,-2.006955,0,0,9.034814486710001,-8.964133869423,2.534156772626986,0,0.1243,-0.15146,-0.980617,0,1406.41353,-1394.565689,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 13,
+# tag: 's',
+# style: 'transform:matrix3d(-11.160122,-5.96521,0,0,0.071106,-0.133032,281.621272,-0.025628,0.092363,-0.172799,-0.980617,0,1107.323419,-1642.147169,-10593.282539,1);width:32px;height:11.3778px;background-position:-424px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.7900305,-1.4913025,0,0,7.112397645533001,-10.554494911783001,2.534156772626986,0,0.092363,-0.172799,-0.980617,0,1107.323419,-1642.147169,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 14,
+# tag: 's',
+# style: 'transform:matrix3d(-12.109438,-3.673358,0,0,0.043784,-0.144348,281.621272,-0.025628,0.056877,-0.187498,-0.980617,0,765.679491,-1826.621854,-10593.282539,1);width:32px;height:11.3778px;background-position:-456px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.0273595,-0.9183395,0,0,4.916654498837,-11.739253218578002,2.534156772626986,0,0.056877,-0.187498,-0.980617,0,765.679491,-1826.621854,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 15,
+# tag: 's',
+# style: 'transform:matrix3d(-12.593394,-1.240342,0,0,0.014782,-0.15012,281.621272,-0.025628,0.019205,-0.194991,-0.980617,0,394.610928,-1940.900485,-10593.282539,1);width:32px;height:11.3778px;background-position:-488px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.1483485,-0.3100855,0,0,2.531967715696,-12.472879407395,2.534156772626986,0,0.019205,-0.194991,-0.980617,0,394.610928,-1940.900485,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 16,
+# tag: 's',
+# style: 'transform:matrix3d(-12.593394,1.240342,0,0,-0.014782,-0.15012,281.621272,-0.025628,-0.019205,-0.194991,-0.980617,0,8.377689,-1980.5914,-10593.282539,1);width:32px;height:11.3778px;background-position:-520px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.1483485,0.3100855,0,0,0.04998035342300001,-12.7271790998,2.534156772626986,0,-0.019205,-0.194991,-0.980617,0,8.377689,-1980.5914,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 17,
+# tag: 's',
+# style: 'transform:matrix3d(-12.109438,3.673358,0,0,-0.043784,-0.144348,281.621272,-0.025628,-0.056877,-0.187498,-0.980617,0,-378.1775,-1944.169297,-10593.282539,1);width:32px;height:11.3778px;background-position:-552px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.0273595,0.9183395,0,0,-2.4339292425000005,-12.492379685879,2.534156772626986,0,-0.056877,-0.187498,-0.980617,0,-378.1775,-1944.169297,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 18,
+# tag: 's',
+# style: 'transform:matrix3d(-11.160122,5.96521,0,0,-0.071106,-0.133032,281.621272,-0.025628,-0.092363,-0.172799,-0.980617,0,-750.199539,-1833.033859,-10593.282539,1);width:32px;height:11.3778px;background-position:-584px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.7900305,1.4913025,0,0,-4.824304946373,-11.777505934613,2.534156772626986,0,-0.092363,-0.172799,-0.980617,0,-750.199539,-1833.033859,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 19,
+# tag: 's',
+# style: 'transform:matrix3d(-9.781928,8.02782,0,0,-0.095692,-0.116606,281.621272,-0.025628,-0.1243,-0.15146,-0.980617,0,-1093.391831,-1651.455958,-10593.282539,1);width:32px;height:11.3778px;background-position:-616px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.445482,2.006955,0,0,-7.029284461216999,-10.610029822906,2.534156772626986,0,-0.1243,-0.15146,-0.980617,0,-1093.391831,-1651.455958,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 20,
+# tag: 's',
+# style: 'transform:matrix3d(-8.02782,9.781928,0,0,-0.116606,-0.095692,281.621272,-0.025628,-0.15146,-0.1243,-0.980617,0,-1394.565689,-1406.41353,-10593.282539,1);width:32px;height:11.3778px;background-position:-648px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.006955,2.445482,0,0,-8.964133869423,-9.034814486710001,2.534156772626986,0,-0.15146,-0.1243,-0.980617,0,-1394.565689,-1406.41353,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 21,
+# tag: 's',
+# style: 'transform:matrix3d(-5.96521,11.160122,0,0,-0.133032,-0.071106,281.621272,-0.025628,-0.172799,-0.092363,-0.980617,0,-1642.147169,-1107.323419,-10593.282539,1);width:32px;height:11.3778px;background-position:-680px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-1.4913025,2.7900305,0,0,-10.554494911783001,-7.112397645533001,2.534156772626986,0,-0.172799,-0.092363,-0.980617,0,-1642.147169,-1107.323419,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 22,
+# tag: 's',
+# style: 'transform:matrix3d(-3.673358,12.109438,0,0,-0.144348,-0.043784,281.621272,-0.025628,-0.187498,-0.056877,-0.980617,0,-1826.621854,-765.679491,-10593.282539,1);width:32px;height:11.3778px;background-position:-712px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-0.9183395,3.0273595,0,0,-11.739253218578002,-4.916654498837,2.534156772626986,0,-0.187498,-0.056877,-0.980617,0,-1826.621854,-765.679491,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 23,
+# tag: 's',
+# style: 'transform:matrix3d(-1.240342,12.593394,0,0,-0.15012,-0.014782,281.621272,-0.025628,-0.194991,-0.019205,-0.980617,0,-1940.900485,-394.610928,-10593.282539,1);width:32px;height:11.3778px;background-position:-744px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-0.3100855,3.1483485,0,0,-12.472879407395,-2.531967715696,2.534156772626986,0,-0.194991,-0.019205,-0.980617,0,-1940.900485,-394.610928,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 24,
+# tag: 's',
+# style: 'transform:matrix3d(1.240342,12.593394,0,0,-0.15012,0.014782,281.621272,-0.025628,-0.194991,0.019205,-0.980617,0,-1980.5914,-8.377689,-10593.282539,1);width:32px;height:11.3778px;background-position:-776px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '0.3100855,3.1483485,0,0,-12.7271790998,-0.04998035342300001,2.534156772626986,0,-0.194991,0.019205,-0.980617,0,-1980.5914,-8.377689,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 25,
+# tag: 's',
+# style: 'transform:matrix3d(3.673358,12.109438,0,0,-0.144348,0.043784,281.621272,-0.025628,-0.187498,0.056877,-0.980617,0,-1944.169297,378.1775,-10593.282539,1);width:32px;height:11.3778px;background-position:-808px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '0.9183395,3.0273595,0,0,-12.492379685879,2.4339292425000005,2.534156772626986,0,-0.187498,0.056877,-0.980617,0,-1944.169297,378.1775,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 26,
+# tag: 's',
+# style: 'transform:matrix3d(5.96521,11.160122,0,0,-0.133032,0.071106,281.621272,-0.025628,-0.172799,0.092363,-0.980617,0,-1833.033859,750.199539,-10593.282539,1);width:32px;height:11.3778px;background-position:-840px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '1.4913025,2.7900305,0,0,-11.777505934613,4.824304946373,2.534156772626986,0,-0.172799,0.092363,-0.980617,0,-1833.033859,750.199539,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 27,
+# tag: 's',
+# style: 'transform:matrix3d(8.02782,9.781928,0,0,-0.116606,0.095692,281.621272,-0.025628,-0.15146,0.1243,-0.980617,0,-1651.455958,1093.391831,-10593.282539,1);width:32px;height:11.3778px;background-position:-872px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.006955,2.445482,0,0,-10.610029822906,7.029284461216999,2.534156772626986,0,-0.15146,0.1243,-0.980617,0,-1651.455958,1093.391831,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 28,
+# tag: 's',
+# style: 'transform:matrix3d(9.781928,8.02782,0,0,-0.095692,0.116606,281.621272,-0.025628,-0.1243,0.15146,-0.980617,0,-1406.41353,1394.565689,-10593.282539,1);width:32px;height:11.3778px;background-position:-904px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.445482,2.006955,0,0,-9.034814486710001,8.964133869423,2.534156772626986,0,-0.1243,0.15146,-0.980617,0,-1406.41353,1394.565689,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 29,
+# tag: 's',
+# style: 'transform:matrix3d(11.160122,5.96521,0,0,-0.071106,0.133032,281.621272,-0.025628,-0.092363,0.172799,-0.980617,0,-1107.323419,1642.147169,-10593.282539,1);width:32px;height:11.3778px;background-position:-936px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.7900305,1.4913025,0,0,-7.112397645533001,10.554494911783001,2.534156772626986,0,-0.092363,0.172799,-0.980617,0,-1107.323419,1642.147169,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 30,
+# tag: 's',
+# style: 'transform:matrix3d(12.109438,3.673358,0,0,-0.043784,0.144348,281.621272,-0.025628,-0.056877,0.187498,-0.980617,0,-765.679491,1826.621854,-10593.282539,1);width:32px;height:11.3778px;background-position:-968px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.0273595,0.9183395,0,0,-4.916654498837,11.739253218578002,2.534156772626986,0,-0.056877,0.187498,-0.980617,0,-765.679491,1826.621854,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 1,
+# longitudeIndex: 31,
+# tag: 's',
+# style: 'transform:matrix3d(12.593394,1.240342,0,0,-0.014782,0.15012,281.621272,-0.025628,-0.019205,0.194991,-0.980617,0,-394.610928,1940.900485,-10593.282539,1);width:32px;height:11.3778px;background-position:-1000px -8.1778px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.1483485,0.3100855,0,0,-2.531967715696,12.472879407395,2.534156772626986,0,-0.019205,0.194991,-0.980617,0,-394.610928,1940.900485,-10593.282539,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.006407,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 0,
+# tag: 's',
+# style: 'transform:matrix3d(17.572444,-1.730734,0,0,0.011396,0.115684,220.880172,-0.019756,0.02551,0.259005,-0.965539,0,-11.461806,2765.97444,-10438.284908,1);width:32px;height:11.3778px;background-position:-8px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '4.393111,-0.4326835,0,0,-0.053760859833999995,13.690068759159999,3.665353839388004,0,0.02551,0.259005,-0.965539,0,-11.461806,2765.97444,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 1,
+# tag: 's',
+# style: 'transform:matrix3d(16.897146,-5.125692,0,0,0.033744,0.11124,220.880172,-0.019756,0.075549,0.249051,-0.965539,0,528.373274,2715.063104,-10438.284908,1);width:32px;height:11.3778px;background-position:-40px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '4.2242865,-1.281423,0,0,2.618071600286,13.437506670656,3.665353839388004,0,0.075549,0.249051,-0.965539,0,528.373274,2715.063104,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 2,
+# tag: 's',
+# style: 'transform:matrix3d(15.572498,-8.323674,0,0,0.054798,0.102522,220.880172,-0.019756,0.122685,0.229527,-0.965539,0,1047.903265,2559.813415,-10438.284908,1);width:32px;height:11.3778px;background-position:-72px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.8931245,-2.0809185,0,0,5.189293725835,12.668548956685,3.665353839388004,0,0.122685,0.229527,-0.965539,0,1047.903265,2559.813415,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 3,
+# tag: 's',
+# style: 'transform:matrix3d(13.649408,-11.20178,0,0,0.073744,0.08986,220.880172,-0.019756,0.165106,0.201182,-0.965539,0,1527.162921,2306.191533,-10438.284908,1);width:32px;height:11.3778px;background-position:-104px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.412352,-2.800445,0,0,7.5610936668190005,11.412744981487,3.665353839388004,0,0.165106,0.201182,-0.965539,0,1527.162921,2306.191533,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 4,
+# tag: 's',
+# style: 'transform:matrix3d(11.20178,-13.649408,0,0,0.08986,0.073744,220.880172,-0.019756,0.201182,0.165106,-0.965539,0,1947.734563,1963.944003,-10438.284908,1);width:32px;height:11.3778px;background-position:-136px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.800445,-3.412352,0,0,9.642326006657,9.718355430816999,3.665353839388004,0,0.201182,0.165106,-0.965539,0,1947.734563,1963.944003,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 5,
+# tag: 's',
+# style: 'transform:matrix3d(8.323674,-15.572498,0,0,0.102522,0.054798,220.880172,-0.019756,0.229527,0.122685,-0.965539,0,2293.455857,1546.223207,-10438.284908,1);width:32px;height:11.3778px;background-position:-168px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.0809185,-3.8931245,0,0,11.353008977722999,7.650495919373,3.665353839388004,0,0.229527,0.122685,-0.965539,0,2293.455857,1546.223207,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 6,
+# tag: 's',
+# style: 'transform:matrix3d(5.125692,-16.897146,0,0,0.11124,0.033744,220.880172,-0.019756,0.249051,0.075549,-0.965539,0,2551.040929,1069.08192,-10438.284908,1);width:32px;height:11.3778px;background-position:-200px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '1.281423,-4.2242865,0,0,12.627401148331,5.28863160288,3.665353839388004,0,0.249051,0.075549,-0.965539,0,2551.040929,1069.08192,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 7,
+# tag: 's',
+# style: 'transform:matrix3d(1.730734,-17.572444,0,0,0.115684,0.011396,220.880172,-0.019756,0.259005,0.02551,-0.965539,0,2710.590929,550.856414,-10438.284908,1);width:32px;height:11.3778px;background-position:-232px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '0.4326835,-4.393111,0,0,13.416529598331,2.7235288287459998,3.665353839388004,0,0.259005,0.02551,-0.965539,0,2710.590929,550.856414,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 8,
+# tag: 's',
+# style: 'transform:matrix3d(-1.730734,-17.572444,0,0,0.115684,-0.011396,220.880172,-0.019756,0.259005,-0.02551,-0.965539,0,2765.97444,11.461806,-10438.284908,1);width:32px;height:11.3778px;background-position:-264px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-0.4326835,-4.393111,0,0,13.690068759159999,0.053760859833999995,3.665353839388004,0,0.259005,-0.02551,-0.965539,0,2765.97444,11.461806,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 9,
+# tag: 's',
+# style: 'transform:matrix3d(-5.125692,-16.897146,0,0,0.11124,-0.033744,220.880172,-0.019756,0.249051,-0.075549,-0.965539,0,2715.063104,-528.373274,-10438.284908,1);width:32px;height:11.3778px;background-position:-296px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-1.281423,-4.2242865,0,0,13.437506670656,-2.618071600286,3.665353839388004,0,0.249051,-0.075549,-0.965539,0,2715.063104,-528.373274,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 10,
+# tag: 's',
+# style: 'transform:matrix3d(-8.323674,-15.572498,0,0,0.102522,-0.054798,220.880172,-0.019756,0.229527,-0.122685,-0.965539,0,2559.813415,-1047.903265,-10438.284908,1);width:32px;height:11.3778px;background-position:-328px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.0809185,-3.8931245,0,0,12.668548956685,-5.189293725835,3.665353839388004,0,0.229527,-0.122685,-0.965539,0,2559.813415,-1047.903265,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 11,
+# tag: 's',
+# style: 'transform:matrix3d(-11.20178,-13.649408,0,0,0.08986,-0.073744,220.880172,-0.019756,0.201182,-0.165106,-0.965539,0,2306.191533,-1527.162921,-10438.284908,1);width:32px;height:11.3778px;background-position:-360px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.800445,-3.412352,0,0,11.412744981487,-7.5610936668190005,3.665353839388004,0,0.201182,-0.165106,-0.965539,0,2306.191533,-1527.162921,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 12,
+# tag: 's',
+# style: 'transform:matrix3d(-13.649408,-11.20178,0,0,0.073744,-0.08986,220.880172,-0.019756,0.165106,-0.201182,-0.965539,0,1963.944003,-1947.734563,-10438.284908,1);width:32px;height:11.3778px;background-position:-392px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.412352,-2.800445,0,0,9.718355430816999,-9.642326006657,3.665353839388004,0,0.165106,-0.201182,-0.965539,0,1963.944003,-1947.734563,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 13,
+# tag: 's',
+# style: 'transform:matrix3d(-15.572498,-8.323674,0,0,0.054798,-0.102522,220.880172,-0.019756,0.122685,-0.229527,-0.965539,0,1546.223207,-2293.455857,-10438.284908,1);width:32px;height:11.3778px;background-position:-424px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.8931245,-2.0809185,0,0,7.650495919373,-11.353008977722999,3.665353839388004,0,0.122685,-0.229527,-0.965539,0,1546.223207,-2293.455857,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 14,
+# tag: 's',
+# style: 'transform:matrix3d(-16.897146,-5.125692,0,0,0.033744,-0.11124,220.880172,-0.019756,0.075549,-0.249051,-0.965539,0,1069.08192,-2551.040929,-10438.284908,1);width:32px;height:11.3778px;background-position:-456px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-4.2242865,-1.281423,0,0,5.28863160288,-12.627401148331,3.665353839388004,0,0.075549,-0.249051,-0.965539,0,1069.08192,-2551.040929,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 15,
+# tag: 's',
+# style: 'transform:matrix3d(-17.572444,-1.730734,0,0,0.011396,-0.115684,220.880172,-0.019756,0.02551,-0.259005,-0.965539,0,550.856414,-2710.590929,-10438.284908,1);width:32px;height:11.3778px;background-position:-488px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-4.393111,-0.4326835,0,0,2.7235288287459998,-13.416529598331,3.665353839388004,0,0.02551,-0.259005,-0.965539,0,550.856414,-2710.590929,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 16,
+# tag: 's',
+# style: 'transform:matrix3d(-17.572444,1.730734,0,0,-0.011396,-0.115684,220.880172,-0.019756,-0.02551,-0.259005,-0.965539,0,11.461806,-2765.97444,-10438.284908,1);width:32px;height:11.3778px;background-position:-520px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-4.393111,0.4326835,0,0,0.053760859833999995,-13.690068759159999,3.665353839388004,0,-0.02551,-0.259005,-0.965539,0,11.461806,-2765.97444,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 17,
+# tag: 's',
+# style: 'transform:matrix3d(-16.897146,5.125692,0,0,-0.033744,-0.11124,220.880172,-0.019756,-0.075549,-0.249051,-0.965539,0,-528.373274,-2715.063104,-10438.284908,1);width:32px;height:11.3778px;background-position:-552px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-4.2242865,1.281423,0,0,-2.618071600286,-13.437506670656,3.665353839388004,0,-0.075549,-0.249051,-0.965539,0,-528.373274,-2715.063104,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 18,
+# tag: 's',
+# style: 'transform:matrix3d(-15.572498,8.323674,0,0,-0.054798,-0.102522,220.880172,-0.019756,-0.122685,-0.229527,-0.965539,0,-1047.903265,-2559.813415,-10438.284908,1);width:32px;height:11.3778px;background-position:-584px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.8931245,2.0809185,0,0,-5.189293725835,-12.668548956685,3.665353839388004,0,-0.122685,-0.229527,-0.965539,0,-1047.903265,-2559.813415,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 19,
+# tag: 's',
+# style: 'transform:matrix3d(-13.649408,11.20178,0,0,-0.073744,-0.08986,220.880172,-0.019756,-0.165106,-0.201182,-0.965539,0,-1527.162921,-2306.191533,-10438.284908,1);width:32px;height:11.3778px;background-position:-616px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.412352,2.800445,0,0,-7.5610936668190005,-11.412744981487,3.665353839388004,0,-0.165106,-0.201182,-0.965539,0,-1527.162921,-2306.191533,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 20,
+# tag: 's',
+# style: 'transform:matrix3d(-11.20178,13.649408,0,0,-0.08986,-0.073744,220.880172,-0.019756,-0.201182,-0.165106,-0.965539,0,-1947.734563,-1963.944003,-10438.284908,1);width:32px;height:11.3778px;background-position:-648px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.800445,3.412352,0,0,-9.642326006657,-9.718355430816999,3.665353839388004,0,-0.201182,-0.165106,-0.965539,0,-1947.734563,-1963.944003,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 21,
+# tag: 's',
+# style: 'transform:matrix3d(-8.323674,15.572498,0,0,-0.102522,-0.054798,220.880172,-0.019756,-0.229527,-0.122685,-0.965539,0,-2293.455857,-1546.223207,-10438.284908,1);width:32px;height:11.3778px;background-position:-680px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.0809185,3.8931245,0,0,-11.353008977722999,-7.650495919373,3.665353839388004,0,-0.229527,-0.122685,-0.965539,0,-2293.455857,-1546.223207,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 22,
+# tag: 's',
+# style: 'transform:matrix3d(-5.125692,16.897146,0,0,-0.11124,-0.033744,220.880172,-0.019756,-0.249051,-0.075549,-0.965539,0,-2551.040929,-1069.08192,-10438.284908,1);width:32px;height:11.3778px;background-position:-712px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-1.281423,4.2242865,0,0,-12.627401148331,-5.28863160288,3.665353839388004,0,-0.249051,-0.075549,-0.965539,0,-2551.040929,-1069.08192,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 23,
+# tag: 's',
+# style: 'transform:matrix3d(-1.730734,17.572444,0,0,-0.115684,-0.011396,220.880172,-0.019756,-0.259005,-0.02551,-0.965539,0,-2710.590929,-550.856414,-10438.284908,1);width:32px;height:11.3778px;background-position:-744px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-0.4326835,4.393111,0,0,-13.416529598331,-2.7235288287459998,3.665353839388004,0,-0.259005,-0.02551,-0.965539,0,-2710.590929,-550.856414,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 24,
+# tag: 's',
+# style: 'transform:matrix3d(1.730734,17.572444,0,0,-0.115684,0.011396,220.880172,-0.019756,-0.259005,0.02551,-0.965539,0,-2765.97444,-11.461806,-10438.284908,1);width:32px;height:11.3778px;background-position:-776px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '0.4326835,4.393111,0,0,-13.690068759159999,-0.053760859833999995,3.665353839388004,0,-0.259005,0.02551,-0.965539,0,-2765.97444,-11.461806,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 25,
+# tag: 's',
+# style: 'transform:matrix3d(5.125692,16.897146,0,0,-0.11124,0.033744,220.880172,-0.019756,-0.249051,0.075549,-0.965539,0,-2715.063104,528.373274,-10438.284908,1);width:32px;height:11.3778px;background-position:-808px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '1.281423,4.2242865,0,0,-13.437506670656,2.618071600286,3.665353839388004,0,-0.249051,0.075549,-0.965539,0,-2715.063104,528.373274,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 26,
+# tag: 's',
+# style: 'transform:matrix3d(8.323674,15.572498,0,0,-0.102522,0.054798,220.880172,-0.019756,-0.229527,0.122685,-0.965539,0,-2559.813415,1047.903265,-10438.284908,1);width:32px;height:11.3778px;background-position:-840px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.0809185,3.8931245,0,0,-12.668548956685,5.189293725835,3.665353839388004,0,-0.229527,0.122685,-0.965539,0,-2559.813415,1047.903265,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 27,
+# tag: 's',
+# style: 'transform:matrix3d(11.20178,13.649408,0,0,-0.08986,0.073744,220.880172,-0.019756,-0.201182,0.165106,-0.965539,0,-2306.191533,1527.162921,-10438.284908,1);width:32px;height:11.3778px;background-position:-872px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.800445,3.412352,0,0,-11.412744981487,7.5610936668190005,3.665353839388004,0,-0.201182,0.165106,-0.965539,0,-2306.191533,1527.162921,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 28,
+# tag: 's',
+# style: 'transform:matrix3d(13.649408,11.20178,0,0,-0.073744,0.08986,220.880172,-0.019756,-0.165106,0.201182,-0.965539,0,-1963.944003,1947.734563,-10438.284908,1);width:32px;height:11.3778px;background-position:-904px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.412352,2.800445,0,0,-9.718355430816999,9.642326006657,3.665353839388004,0,-0.165106,0.201182,-0.965539,0,-1963.944003,1947.734563,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 29,
+# tag: 's',
+# style: 'transform:matrix3d(15.572498,8.323674,0,0,-0.054798,0.102522,220.880172,-0.019756,-0.122685,0.229527,-0.965539,0,-1546.223207,2293.455857,-10438.284908,1);width:32px;height:11.3778px;background-position:-936px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.8931245,2.0809185,0,0,-7.650495919373,11.353008977722999,3.665353839388004,0,-0.122685,0.229527,-0.965539,0,-1546.223207,2293.455857,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 30,
+# tag: 's',
+# style: 'transform:matrix3d(16.897146,5.125692,0,0,-0.033744,0.11124,220.880172,-0.019756,-0.075549,0.249051,-0.965539,0,-1069.08192,2551.040929,-10438.284908,1);width:32px;height:11.3778px;background-position:-968px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '4.2242865,1.281423,0,0,-5.28863160288,12.627401148331,3.665353839388004,0,-0.075549,0.249051,-0.965539,0,-1069.08192,2551.040929,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 2,
+# longitudeIndex: 31,
+# tag: 's',
+# style: 'transform:matrix3d(17.572444,1.730734,0,0,-0.011396,0.115684,220.880172,-0.019756,-0.02551,0.259005,-0.965539,0,-550.856414,2710.590929,-10438.284908,1);width:32px;height:11.3778px;background-position:-1000px -35.3px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '4.393111,0.4326835,0,0,-2.7235288287459998,13.416529598331,3.665353839388004,0,-0.02551,0.259005,-0.965539,0,-550.856414,2710.590929,-10438.284908,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.004939,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 0,
+# tag: 's',
+# style: 'transform:matrix3d(22.466062,-2.212714,0,0,0.009196,0.09338,182.424414,-0.015946,0.03173,0.322158,-0.946154,0,-14.492979,3537.881977,-10232.433244,1);width:32px;height:11.3778px;background-position:-8px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '5.6165155,-0.5531785,0,0,-0.055477260783499995,14.1271115013105,4.814508372794009,0,0.03173,0.322158,-0.946154,0,-14.492979,3537.881977,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 1,
+# tag: 's',
+# style: 'transform:matrix3d(21.602704,-6.553108,0,0,0.027236,0.089792,182.424414,-0.015946,0.09397,0.309778,-0.946154,0,675.992033,3472.730007,-10232.433244,1);width:32px;height:11.3778px;background-position:-40px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '5.400676,-1.638277,0,0,2.7016512395545,13.8664861729055,4.814508372794009,0,0.09397,0.309778,-0.946154,0,675.992033,3472.730007,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 2,
+# tag: 's',
+# style: 'transform:matrix3d(19.909166,-10.641672,0,0,0.04423,0.08275,182.424414,-0.015946,0.152599,0.285493,-0.946154,0,1340.499051,3274.12297,-10232.433244,1);width:32px;height:11.3778px;background-position:-72px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '4.9772915,-2.660418,0,0,5.354956966811499,13.072978719904997,4.814508372794009,0,0.152599,0.285493,-0.946154,0,1340.499051,3274.12297,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 3,
+# tag: 's',
+# style: 'transform:matrix3d(17.45053,-14.32128,0,0,0.059524,0.072534,182.424414,-0.015946,0.205364,0.250237,-0.946154,0,1953.491442,2949.693223,-10232.433244,1);width:32px;height:11.3778px;background-position:-104px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '4.3626325,-3.58032,0,0,7.8024746335329995,11.7770855334895,4.814508372794009,0,0.205364,0.250237,-0.946154,0,1953.491442,2949.693223,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 4,
+# tag: 's',
+# style: 'transform:matrix3d(14.32128,-17.45053,0,0,0.072534,0.059524,182.424414,-0.015946,0.250237,0.205364,-0.946154,0,2491.412253,2511.908421,-10232.433244,1);width:32px;height:11.3778px;background-position:-136px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '3.58032,-4.3626325,0,0,9.950148446584498,10.0286039203165,4.814508372794009,0,0.250237,0.205364,-0.946154,0,2491.412253,2511.908421,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 5,
+# tag: 's',
+# style: 'transform:matrix3d(10.641672,-19.909166,0,0,0.08275,0.04423,182.424414,-0.015946,0.285493,0.152599,-0.946154,0,2933.589488,1977.592386,-10232.433244,1);width:32px;height:11.3778px;background-position:-168px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '2.660418,-4.9772915,0,0,11.715441993911998,7.894729546788999,4.814508372794009,0,0.285493,0.152599,-0.946154,0,2933.589488,1977.592386,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 6,
+# tag: 's',
+# style: 'transform:matrix3d(6.553108,-21.602704,0,0,0.089792,0.027236,182.424414,-0.015946,0.309778,0.09397,-0.946154,0,3263.030524,1367.278585,-10232.433244,1);width:32px;height:11.3778px;background-position:-200px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '1.638277,-5.400676,0,0,13.030519183925998,5.457465079102499,4.814508372794009,0,0.309778,0.09397,-0.946154,0,3263.030524,1367.278585,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 7,
+# tag: 's',
+# style: 'transform:matrix3d(2.212714,-22.466062,0,0,0.09338,0.009196,182.424414,-0.015946,0.322158,0.03173,-0.946154,0,3467.075126,704.421035,-10232.433244,1);width:32px;height:11.3778px;background-position:-232px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '0.5531785,-5.6165155,0,0,13.844839989799,2.8104734560274993,4.814508372794009,0,0.322158,0.03173,-0.946154,0,3467.075126,704.421035,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 8,
+# tag: 's',
+# style: 'transform:matrix3d(-2.212714,-22.466062,0,0,0.09338,-0.009196,182.424414,-0.015946,0.322158,-0.03173,-0.946154,0,3537.881977,14.492979,-10232.433244,1);width:32px;height:11.3778px;background-position:-264px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-0.5531785,-5.6165155,0,0,14.1271115013105,0.055477260783499995,4.814508372794009,0,0.322158,-0.03173,-0.946154,0,3537.881977,14.492979,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 9,
+# tag: 's',
+# style: 'transform:matrix3d(-6.553108,-21.602704,0,0,0.089792,-0.027236,182.424414,-0.015946,0.309778,-0.09397,-0.946154,0,3472.730007,-675.992033,-10232.433244,1);width:32px;height:11.3778px;background-position:-296px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-1.638277,-5.400676,0,0,13.8664861729055,-2.7016512395545,4.814508372794009,0,0.309778,-0.09397,-0.946154,0,3472.730007,-675.992033,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 10,
+# tag: 's',
+# style: 'transform:matrix3d(-10.641672,-19.909166,0,0,0.08275,-0.04423,182.424414,-0.015946,0.285493,-0.152599,-0.946154,0,3274.12297,-1340.499051,-10232.433244,1);width:32px;height:11.3778px;background-position:-328px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.660418,-4.9772915,0,0,13.072978719904997,-5.354956966811499,4.814508372794009,0,0.285493,-0.152599,-0.946154,0,3274.12297,-1340.499051,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 11,
+# tag: 's',
+# style: 'transform:matrix3d(-14.32128,-17.45053,0,0,0.072534,-0.059524,182.424414,-0.015946,0.250237,-0.205364,-0.946154,0,2949.693223,-1953.491442,-10232.433244,1);width:32px;height:11.3778px;background-position:-360px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.58032,-4.3626325,0,0,11.7770855334895,-7.8024746335329995,4.814508372794009,0,0.250237,-0.205364,-0.946154,0,2949.693223,-1953.491442,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 12,
+# tag: 's',
+# style: 'transform:matrix3d(-17.45053,-14.32128,0,0,0.059524,-0.072534,182.424414,-0.015946,0.205364,-0.250237,-0.946154,0,2511.908421,-2491.412253,-10232.433244,1);width:32px;height:11.3778px;background-position:-392px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-4.3626325,-3.58032,0,0,10.0286039203165,-9.950148446584498,4.814508372794009,0,0.205364,-0.250237,-0.946154,0,2511.908421,-2491.412253,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 13,
+# tag: 's',
+# style: 'transform:matrix3d(-19.909166,-10.641672,0,0,0.04423,-0.08275,182.424414,-0.015946,0.152599,-0.285493,-0.946154,0,1977.592386,-2933.589488,-10232.433244,1);width:32px;height:11.3778px;background-position:-424px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-4.9772915,-2.660418,0,0,7.894729546788999,-11.715441993911998,4.814508372794009,0,0.152599,-0.285493,-0.946154,0,1977.592386,-2933.589488,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 14,
+# tag: 's',
+# style: 'transform:matrix3d(-21.602704,-6.553108,0,0,0.027236,-0.089792,182.424414,-0.015946,0.09397,-0.309778,-0.946154,0,1367.278585,-3263.030524,-10232.433244,1);width:32px;height:11.3778px;background-position:-456px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-5.400676,-1.638277,0,0,5.457465079102499,-13.030519183925998,4.814508372794009,0,0.09397,-0.309778,-0.946154,0,1367.278585,-3263.030524,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 15,
+# tag: 's',
+# style: 'transform:matrix3d(-22.466062,-2.212714,0,0,0.009196,-0.09338,182.424414,-0.015946,0.03173,-0.322158,-0.946154,0,704.421035,-3467.075126,-10232.433244,1);width:32px;height:11.3778px;background-position:-488px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-5.6165155,-0.5531785,0,0,2.8104734560274993,-13.844839989799,4.814508372794009,0,0.03173,-0.322158,-0.946154,0,704.421035,-3467.075126,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 16,
+# tag: 's',
+# style: 'transform:matrix3d(-22.466062,2.212714,0,0,-0.009196,-0.09338,182.424414,-0.015946,-0.03173,-0.322158,-0.946154,0,14.492979,-3537.881977,-10232.433244,1);width:32px;height:11.3778px;background-position:-520px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-5.6165155,0.5531785,0,0,0.055477260783499995,-14.1271115013105,4.814508372794009,0,-0.03173,-0.322158,-0.946154,0,14.492979,-3537.881977,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 17,
+# tag: 's',
+# style: 'transform:matrix3d(-21.602704,6.553108,0,0,-0.027236,-0.089792,182.424414,-0.015946,-0.09397,-0.309778,-0.946154,0,-675.992033,-3472.730007,-10232.433244,1);width:32px;height:11.3778px;background-position:-552px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-5.400676,1.638277,0,0,-2.7016512395545,-13.8664861729055,4.814508372794009,0,-0.09397,-0.309778,-0.946154,0,-675.992033,-3472.730007,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 18,
+# tag: 's',
+# style: 'transform:matrix3d(-19.909166,10.641672,0,0,-0.04423,-0.08275,182.424414,-0.015946,-0.152599,-0.285493,-0.946154,0,-1340.499051,-3274.12297,-10232.433244,1);width:32px;height:11.3778px;background-position:-584px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-4.9772915,2.660418,0,0,-5.354956966811499,-13.072978719904997,4.814508372794009,0,-0.152599,-0.285493,-0.946154,0,-1340.499051,-3274.12297,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 19,
+# tag: 's',
+# style: 'transform:matrix3d(-17.45053,14.32128,0,0,-0.059524,-0.072534,182.424414,-0.015946,-0.205364,-0.250237,-0.946154,0,-1953.491442,-2949.693223,-10232.433244,1);width:32px;height:11.3778px;background-position:-616px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-4.3626325,3.58032,0,0,-7.8024746335329995,-11.7770855334895,4.814508372794009,0,-0.205364,-0.250237,-0.946154,0,-1953.491442,-2949.693223,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 20,
+# tag: 's',
+# style: 'transform:matrix3d(-14.32128,17.45053,0,0,-0.072534,-0.059524,182.424414,-0.015946,-0.250237,-0.205364,-0.946154,0,-2491.412253,-2511.908421,-10232.433244,1);width:32px;height:11.3778px;background-position:-648px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-3.58032,4.3626325,0,0,-9.950148446584498,-10.0286039203165,4.814508372794009,0,-0.250237,-0.205364,-0.946154,0,-2491.412253,-2511.908421,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 21,
+# tag: 's',
+# style: 'transform:matrix3d(-10.641672,19.909166,0,0,-0.08275,-0.04423,182.424414,-0.015946,-0.285493,-0.152599,-0.946154,0,-2933.589488,-1977.592386,-10232.433244,1);width:32px;height:11.3778px;background-position:-680px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-2.660418,4.9772915,0,0,-11.715441993911998,-7.894729546788999,4.814508372794009,0,-0.285493,-0.152599,-0.946154,0,-2933.589488,-1977.592386,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 22,
+# tag: 's',
+# style: 'transform:matrix3d(-6.553108,21.602704,0,0,-0.089792,-0.027236,182.424414,-0.015946,-0.309778,-0.09397,-0.946154,0,-3263.030524,-1367.278585,-10232.433244,1);width:32px;height:11.3778px;background-position:-712px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-1.638277,5.400676,0,0,-13.030519183925998,-5.457465079102499,4.814508372794009,0,-0.309778,-0.09397,-0.946154,0,-3263.030524,-1367.278585,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865,0,0,1,0,0,0,0,1'
+# }
+# },
+# {
+# latitudeIndex: 3,
+# longitudeIndex: 23,
+# tag: 's',
+# style: 'transform:matrix3d(-2.212714,22.466062,0,0,-0.09338,-0.009196,182.424414,-0.015946,-0.322158,-0.03173,-0.946154,0,-3467.075126,-704.421035,-10232.433244,1);width:32px;height:11.3778px;background-position:-744px -62.4222px;background-size:1040px 839px',
+# projectiveTextureLayer: {
+# schema: 'polycss-prepared-projective-texture-layer@1',
+# rasterScale: 4,
+# frameMatrix: '-0.5531785,5.6165155,0,0,-13.844839989799,-2.8104734560274993,4.814508372794009,0,-0.322158,-0.03173,-0.946154,0,-3467.075126,-704.421035,-10232.433244,1',
+# textureMatrix: '1,0,0,0,0,1,0,-0.0039865
+# Subtest: site/test/projective-surface-seam-contract.test.mts
+not ok 64 - site/test/projective-surface-seam-contract.test.mts
+ ---
+ duration_ms: 178.902458
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/projective-surface-seam-contract.test.mts:1:1'
+ failureType: 'testCodeFailure'
+ exitCode: 1
+ signal: ~
+ error: 'test failed'
+ code: 'ERR_TEST_FAILURE'
+ ...
+# Subtest: keeps reversal and every camera-advancing present in its recorded step
+ok 288 - keeps reversal and every camera-advancing present in its recorded step
+ ---
+ duration_ms: 1.158916
+ type: 'test'
+ ...
+# Subtest: advances uncaptured native frames instead of skipping their camera work
+ok 289 - advances uncaptured native frames instead of skipping their camera work
+ ---
+ duration_ms: 0.261209
+ type: 'test'
+ ...
+# Subtest: rejects missing frame clocks and an already moving start
+ok 290 - rejects missing frame clocks and an already moving start
+ ---
+ duration_ms: 0.491583
+ type: 'test'
+ ...
+# Subtest: does not replay a preceding gesture still retained before the new press
+ok 291 - does not replay a preceding gesture still retained before the new press
+ ---
+ duration_ms: 0.160333
+ type: 'test'
+ ...
+# Subtest: ignores a pointer point stored after a non-launching release
+ok 292 - ignores a pointer point stored after a non-launching release
+ ---
+ duration_ms: 0.078417
+ type: 'test'
+ ...
+# Subtest: pairs a fresh press during coast, held jitter and a second release independently
+ok 293 - pairs a fresh press during coast, held jitter and a second release independently
+ ---
+ duration_ms: 0.220541
+ type: 'test'
+ ...
+# Subtest: binds click-only receipts without fabricating a drag launch
+ok 294 - binds click-only receipts without fabricating a drag launch
+ ---
+ duration_ms: 0.719917
+ type: 'test'
+ ...
+# Subtest: advances an uncaptured receipt-only present
+ok 295 - advances an uncaptured receipt-only present
+ ---
+ duration_ms: 0.260375
+ type: 'test'
+ ...
+# Subtest: binds click receipts before a later drag without treating clicks as launches
+ok 296 - binds click receipts before a later drag without treating clicks as launches
+ ---
+ duration_ms: 0.29325
+ type: 'test'
+ ...
+# Subtest: pairs a source-consumed drag and following stop click
+ok 297 - pairs a source-consumed drag and following stop click
+ ---
+ duration_ms: 0.274792
+ type: 'test'
+ ...
+# Subtest: derives every root planet lane from the validated catalog
+ok 298 - derives every root planet lane from the validated catalog
+ ---
+ duration_ms: 1728.85325
+ type: 'test'
+ ...
+# Subtest: loads object-owned browser and audit expectations for every implementation
+not ok 299 - loads object-owned browser and audit expectations for every implementation
+ ---
+ duration_ms: 355.854167
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/root-orchestration.test.mts:36:1'
+ failureType: 'testCodeFailure'
+ error: 'ryugu: browser profile must declare retained interaction evidence'
+ code: 'ERR_ASSERTION'
+ name: 'AssertionError'
+ expected: true
+ actual: false
+ operator: '=='
+ stack: |-
+ validatePlanetBrowserProfile (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.mts:51:10)
+ loadPlanetBrowserProfile (file:///private/tmp/cssearth-pr390/site/test/load-browser-profile.mts:19:10)
+ async Promise.all (index 122)
+ async TestContext. (file:///private/tmp/cssearth-pr390/site/test/root-orchestration.test.mts:38:20)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# file:///private/tmp/cssearth-pr390/site/prepared-context-objects.mts:2
+# export const CONTEXT_OBJECT_DESCRIPTORS = import.meta.glob(['../src/objects/betelgeuse-shell/object.json', '../src/objects/galaxy-clusters/object.json', '../src/objects/hd-181327-disc/object.json', '../src/objects/heliosphere/object.json', '../src/objects/helix/object.json', '../src/objects/lmc/object.json', '../src/objects/local-group/object.json', '../src/objects/m1/object.json', '../src/objects/m2-9/object.json', '../src/objects/m31/object.json', '../src/objects/m33/object.json', '../src/objects/m42/object.json', '../src/objects/m45/object.json', '../src/objects/m8/object.json', '../src/objects/milky-way/object.json', '../src/objects/nearby-universe/object.json', '../src/objects/omega-centauri/object.json', '../src/objects/smc/object.json', '../src/objects/stellar-neighbourhood/object.json'], { eager: true, import: 'default' });
+# ^
+# TypeError: (intermediate value).glob is not a function
+# at file:///private/tmp/cssearth-pr390/site/prepared-context-objects.mts:2:80
+# at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
+# at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
+# at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)
+# Node.js v22.23.2
+# Subtest: site/test/router-runtime.test.mts
+not ok 67 - site/test/router-runtime.test.mts
+ ---
+ duration_ms: 273.245334
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/router-runtime.test.mts:1:1'
+ failureType: 'testCodeFailure'
+ exitCode: 1
+ signal: ~
+ error: 'test failed'
+ code: 'ERR_TEST_FAILURE'
+ ...
+# node:internal/modules/esm/resolve:275
+# throw new ERR_MODULE_NOT_FOUND(
+# ^
+# Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/private/tmp/cssearth-pr390/src/renderers/css/stars/opacity-clock.js' imported from /private/tmp/cssearth-pr390/src/renderers/css/navigation/prepared-wheel-zoom.ts
+# at finalizeResolution (node:internal/modules/esm/resolve:275:11)
+# at moduleResolve (node:internal/modules/esm/resolve:861:10)
+# at defaultResolve (node:internal/modules/esm/resolve:985:11)
+# at \#cachedDefaultResolve (node:internal/modules/esm/loader:747:20)
+# at ModuleLoader.resolve (node:internal/modules/esm/loader:724:38)
+# at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:320:38)
+# at ModuleJob._link (node:internal/modules/esm/module_job:182:49) {
+# code: 'ERR_MODULE_NOT_FOUND',
+# url: 'file:///private/tmp/cssearth-pr390/src/renderers/css/stars/opacity-clock.js'
+# }
+# Node.js v22.23.2
+# Subtest: site/test/runtime-policy.test.mts
+not ok 68 - site/test/runtime-policy.test.mts
+ ---
+ duration_ms: 66.326667
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/runtime-policy.test.mts:1:1'
+ failureType: 'testCodeFailure'
+ exitCode: 1
+ signal: ~
+ error: 'test failed'
+ code: 'ERR_TEST_FAILURE'
+ ...
+# Subtest: asteroid, trans-Neptunian and comet Shadows default off in authored content and prepared runtime
+ok 302 - asteroid, trans-Neptunian and comet Shadows default off in authored content and prepared runtime
+ ---
+ duration_ms: 2388.257
+ type: 'test'
+ ...
+# Subtest: publishes one off-by-default Shadows control for every planet
+ok 303 - publishes one off-by-default Shadows control for every planet
+ ---
+ duration_ms: 75.928084
+ type: 'test'
+ ...
+# Subtest: control content permits empty capabilities but rejects malformed or conflicting declarations
+ok 304 - control content permits empty capabilities but rejects malformed or conflicting declarations
+ ---
+ duration_ms: 0.336541
+ type: 'test'
+ ...
+# Subtest: readiness waits for native activation and restoration; one session commands playback and teardown
+ok 305 - readiness waits for native activation and restoration; one session commands playback and teardown
+ ---
+ duration_ms: 1.2325
+ type: 'test'
+ ...
+# Subtest: a late factory cannot mount or replace the next session after cancellation
+ok 306 - a late factory cannot mount or replace the next session after cancellation
+ ---
+ duration_ms: 0.188292
+ type: 'test'
+ ...
+# Subtest: a transferred prepared bank survives request completion and releases on scene teardown
+ok 307 - a transferred prepared bank survives request completion and releases on scene teardown
+ ---
+ duration_ms: 0.611
+ type: 'test'
+ ...
+# Subtest: failed activation releases transferred preparation even when the factory throws before returning a mount
+ok 308 - failed activation releases transferred preparation even when the factory throws before returning a mount
+ ---
+ duration_ms: 0.296458
+ type: 'test'
+ ...
+# Subtest: cancellation before handoff releases a late bank and forbids transfer
+ok 309 - cancellation before handoff releases a late bank and forbids transfer
+ ---
+ duration_ms: 0.213375
+ type: 'test'
+ ...
+# Subtest: URL replacement stays bounded and flush failures cannot skip native or binding cleanup
+ok 310 - URL replacement stays bounded and flush failures cannot skip native or binding cleanup
+ ---
+ duration_ms: 0.280416
+ type: 'test'
+ ...
+# Subtest: every scene links its own existing README at the build revision
+ok 311 - every scene links its own existing README at the build revision
+ ---
+ duration_ms: 131.9055
+ type: 'test'
+ ...
+# Subtest: overview and catalogue selections link their actual documentation owner
+ok 312 - overview and catalogue selections link their actual documentation owner
+ ---
+ duration_ms: 7.5835
+ type: 'test'
+ ...
+# Subtest: documentation preparation rejects missing owners and paths outside object packages
+ok 313 - documentation preparation rejects missing owners and paths outside object packages
+ ---
+ duration_ms: 0.27575
+ type: 'test'
+ ...
+# Subtest: the footer prepares compact provider credits without changing the README destination
+ok 314 - the footer prepares compact provider credits without changing the README destination
+ ---
+ duration_ms: 0.069791
+ type: 'test'
+ ...
+# Subtest: the Solar System overview credits its bodies rather than inheriting only the Sun’s maps
+ok 315 - the Solar System overview credits its bodies rather than inheriting only the Sun’s maps
+ ---
+ duration_ms: 0.233416
+ type: 'test'
+ ...
+# Subtest: publishes the canonical scientific chart order
+ok 316 - publishes the canonical scientific chart order
+ ---
+ duration_ms: 1.278792
+ type: 'test'
+ ...
+# Subtest: orders any applicable subset without inventing missing charts
+ok 317 - orders any applicable subset without inventing missing charts
+ ---
+ duration_ms: 0.160833
+ type: 'test'
+ ...
+# Subtest: keeps unknown future chart types after known charts in source order
+ok 318 - keeps unknown future chart types after known charts in source order
+ ---
+ duration_ms: 0.207208
+ type: 'test'
+ ...
+# Subtest: binds phase charts to explicit primary-source provenance
+ok 319 - binds phase charts to explicit primary-source provenance
+ ---
+ duration_ms: 3.77675
+ type: 'test'
+ ...
+# Subtest: a page that ships its catalogue empty has it fetched and merged before matching
+ok 320 - a page that ships its catalogue empty has it fetched and merged before matching
+ ---
+ duration_ms: 87.946958
+ type: 'test'
+ ...
+# Subtest: a merged catalogue fragment degrades to an empty list plus a message on a byte or hash drift, instead of failing the request
+ok 321 - a merged catalogue fragment degrades to an empty list plus a message on a byte or hash drift, instead of failing the request
+ ---
+ duration_ms: 5.845084
+ type: 'test'
+ ...
+# Subtest: a page that already inlines its catalogue never fetches the fragment
+ok 322 - a page that already inlines its catalogue never fetches the fragment
+ ---
+ duration_ms: 7.673875
+ type: 'test'
+ ...
+# Subtest: native search uses shared matching, retains every row, and preserves the scene/head bytes
+ok 323 - native search uses shared matching, retains every row, and preserves the scene/head bytes
+ ---
+ duration_ms: 30.078708
+ type: 'test'
+ ...
+# Subtest: empty submission browses all objects, categories retain the query, pills replace it
+ok 324 - empty submission browses all objects, categories retain the query, pills replace it
+ ---
+ duration_ms: 6.438875
+ type: 'test'
+ ...
+# Subtest: features are pinned, rendered into existing rows and have ordinary destination links
+ok 325 - features are pinned, rendered into existing rows and have ordinary destination links
+ ---
+ duration_ms: 6.619625
+ type: 'test'
+ ...
+# Subtest: search is a flat list across categories, including queries that name an overview
+ok 326 - search is a flat list across categories, including queries that name an overview
+ ---
+ duration_ms: 3.63
+ type: 'test'
+ ...
+# Subtest: named features share the results panel, start collapsed, and disappear when there are no matches
+ok 327 - named features share the results panel, start collapsed, and disappear when there are no matches
+ ---
+ duration_ms: 6.847959
+ type: 'test'
+ ...
+# Subtest: live feature results retain their rows and disclosure until the query changes
+ok 328 - live feature results retain their rows and disclosure until the query changes
+ ---
+ duration_ms: 2.354125
+ type: 'test'
+ ...
+# Subtest: queries stay text, are bounded, and cannot become executable attributes or scene markup
+ok 329 - queries stay text, are bounded, and cannot become executable attributes or scene markup
+ ---
+ duration_ms: 3.206875
+ type: 'test'
+ ...
+# Subtest: Netlify routing keeps all query parameters, bypasses assets, and never recurses on its function
+ok 330 - Netlify routing keeps all query parameters, bypasses assets, and never recurses on its function
+ ---
+ duration_ms: 1.226666
+ type: 'test'
+ ...
+# Subtest: function fetches only the static page and pinned indexes; search responses are not shared-cacheable
+ok 331 - function fetches only the static page and pinned indexes; search responses are not shared-cacheable
+ ---
+ duration_ms: 2.391291
+ type: 'test'
+ ...
+# Subtest: discovers objects through multiple pages and resolves relative links at their source
+ok 332 - discovers objects through multiple pages and resolves relative links at their source
+ ---
+ duration_ms: 0.951167
+ type: 'test'
+ ...
+# Subtest: rejects the orphaned Sun even when its inspected page retains a self-link
+ok 333 - rejects the orphaned Sun even when its inspected page retains a self-link
+ ---
+ duration_ms: 0.350666
+ type: 'test'
+ ...
+# Subtest: rejects a disconnected cycle even when every object has a link from another page
+ok 334 - rejects a disconnected cycle even when every object has a link from another page
+ ---
+ duration_ms: 0.18125
+ type: 'test'
+ ...
+# Subtest: external links with a matching pathname do not discover a local object
+ok 335 - external links with a matching pathname do not discover a local object
+ ---
+ duration_ms: 0.170916
+ type: 'test'
+ ...
+# Subtest: retained catalogue groups follow filters and release their visibility observer
+ok 336 - retained catalogue groups follow filters and release their visibility observer
+ ---
+ duration_ms: 42.741875
+ type: 'test'
+ ...
+# Subtest: Illustration models starts off and retains its independent preference across body navigation
+ok 337 - Illustration models starts off and retains its independent preference across body navigation
+ ---
+ duration_ms: 1.986292
+ type: 'test'
+ ...
+# Subtest: Asteroids starts off and retains its independent preference across body navigation
+ok 338 - Asteroids starts off and retains its independent preference across body navigation
+ ---
+ duration_ms: 1.897542
+ type: 'test'
+ ...
+# Subtest: Asteroids Orbits starts off and retains its independent preference across body navigation
+ok 339 - Asteroids Orbits starts off and retains its independent preference across body navigation
+ ---
+ duration_ms: 1.317583
+ type: 'test'
+ ...
+# Subtest: Asteroid Labels starts off and retains its independent preference across body navigation
+ok 340 - Asteroid Labels starts off and retains its independent preference across body navigation
+ ---
+ duration_ms: 1.390667
+ type: 'test'
+ ...
+# Subtest: Minimap starts off, mirrors its state for the stylesheet and keeps the preference across body navigation
+ok 341 - Minimap starts off, mirrors its state for the stylesheet and keeps the preference across body navigation
+ ---
+ duration_ms: 1.089875
+ type: 'test'
+ ...
+# Subtest: shell with no optional controls keeps Motion/high contrast and accessible blocked intent
+ok 342 - shell with no optional controls keeps Motion/high contrast and accessible blocked intent
+ ---
+ duration_ms: 0.982125
+ type: 'test'
+ ...
+# Subtest: failed shell construction cleans earlier controllers and their scheduled work
+ok 343 - failed shell construction cleans earlier controllers and their scheduled work
+ ---
+ duration_ms: 8.871125
+ type: 'test'
+ ...
+# Subtest: object content replacement retains shell controls and input state without accumulating listeners
+ok 344 - object content replacement retains shell controls and input state without accumulating listeners
+ ---
+ duration_ms: 3.725625
+ type: 'test'
+ ...
+# Subtest: Motion cannot enable Speed before the shared runtime is ready or after it retires
+ok 345 - Motion cannot enable Speed before the shared runtime is ready or after it retires
+ ---
+ duration_ms: 14.218833
+ type: 'test'
+ ...
+# Subtest: camera zoom preserves native information selections and focus without installing tab listeners
+ok 346 - camera zoom preserves native information selections and focus without installing tab listeners
+ ---
+ duration_ms: 0.943209
+ type: 'test'
+ ...
+# Subtest: destination card stays fixed across flight poses and camera handoff, then follows manual zoom
+ok 347 - destination card stays fixed across flight poses and camera handoff, then follows manual zoom
+ ---
+ duration_ms: 1.103
+ type: 'test'
+ ...
+# Subtest: camera scale keeps the overview, while search hides and dismissal restores its Atlas navigation
+ok 348 - camera scale keeps the overview, while search hides and dismissal restores its Atlas navigation
+ ---
+ duration_ms: 2.887917
+ type: 'test'
+ ...
+# Subtest: the planet lens controller ignores an earlier retained galaxy bank and binds its own lens controls
+ok 349 - the planet lens controller ignores an earlier retained galaxy bank and binds its own lens controls
+ ---
+ duration_ms: 0.611333
+ type: 'test'
+ ...
+# Subtest: a prepared galaxy takes precedence over the retained Milky Way card and clears cleanly on planet return
+ok 350 - a prepared galaxy takes precedence over the retained Milky Way card and clears cleanly on planet return
+ ---
+ duration_ms: 4.049625
+ type: 'test'
+ ...
+# Subtest: clearing search keeps the current object or overview card and permits another search
+ok 351 - clearing search keeps the current object or overview card and permits another search
+ ---
+ duration_ms: 0.688125
+ type: 'test'
+ ...
+# Subtest: flight completion and overview handoff preserve a newer active search
+ok 352 - flight completion and overview handoff preserve a newer active search
+ ---
+ duration_ms: 0.903125
+ type: 'test'
+ ...
+# Subtest: category pills toggle shared results without camera navigation or opening the search sheet
+ok 353 - category pills toggle shared results without camera navigation or opening the search sheet
+ ---
+ duration_ms: 16.193875
+ type: 'test'
+ ...
+# Subtest: changing Illustration models refreshes an open category without erasing its query
+ok 354 - changing Illustration models refreshes an open category without erasing its query
+ ---
+ duration_ms: 0.791333
+ type: 'test'
+ ...
+# Subtest: the overview preview changes immediately and survives a same-scene commit
+ok 355 - the overview preview changes immediately and survives a same-scene commit
+ ---
+ duration_ms: 1.392
+ type: 'test'
+ ...
+# Subtest: an empty search survives dismissal, overview resets, and object commits
+ok 356 - an empty search survives dismissal, overview resets, and object commits
+ ---
+ duration_ms: 0.984
+ type: 'test'
+ ...
+# Subtest: choosing the current body from search shows its card without editing the query
+ok 357 - choosing the current body from search shows its card without editing the query
+ ---
+ duration_ms: 0.543834
+ type: 'test'
+ ...
+# Subtest: a Milky Way breadcrumb previews its own card and preserves the search query
+ok 358 - a Milky Way breadcrumb previews its own card and preserves the search query
+ ---
+ duration_ms: 0.496125
+ type: 'test'
+ ...
+# Subtest: camera handoffs retain filtered results and reset only a scrolled results panel
+ok 359 - camera handoffs retain filtered results and reset only a scrolled results panel
+ ---
+ duration_ms: 0.600416
+ type: 'test'
+ ...
+# Subtest: one source link follows selection, independent of search, dataset and accordion state
+ok 360 - one source link follows selection, independent of search, dataset and accordion state
+ ---
+ duration_ms: 5.029083
+ type: 'test'
+ ...
+# Subtest: a native body request retains its prepared default link
+ok 361 - a native body request retains its prepared default link
+ ---
+ duration_ms: 0.478125
+ type: 'test'
+ ...
+# Subtest: surface consumers use the published clipped viewport without measuring the scene
+ok 362 - surface consumers use the published clipped viewport without measuring the scene
+ ---
+ duration_ms: 0.577417
+ type: 'test'
+ ...
+# Subtest: vendored Cesium helpers reproduce the pinned upstream methods verbatim
+ok 363 - vendored Cesium helpers reproduce the pinned upstream methods verbatim
+ ---
+ duration_ms: 4.941083
+ type: 'test'
+ ...
+# Subtest: flat map cardinal landmarks retain east-west order and north-up orientation
+ok 364 - flat map cardinal landmarks retain east-west order and north-up orientation
+ ---
+ duration_ms: 0.489875
+ type: 'test'
+ ...
+# Subtest: map navigation rotates camera and eye together, retaining distance and roll
+ok 365 - map navigation rotates camera and eye together, retaining distance and roll
+ ---
+ duration_ms: 0.245166
+ type: 'test'
+ ...
+# Subtest: opposite-side clicks and seam crossings stay finite and reach the requested point
+ok 366 - opposite-side clicks and seam crossings stay finite and reach the requested point
+ ---
+ duration_ms: 0.184083
+ type: 'test'
+ ...
+# Subtest: minimap uses the upstream Cesium rectangle for zoom, roll, poles and horizon cases
+ok 367 - minimap uses the upstream Cesium rectangle for zoom, roll, poles and horizon cases
+ ---
+ duration_ms: 7.922792
+ type: 'test'
+ ...
+# Subtest: Cesium whole-globe fallback is retained and the rectangle shrinks with close zoom
+ok 368 - Cesium whole-globe fallback is retained and the rectangle shrinks with close zoom
+ ---
+ duration_ms: 0.366292
+ type: 'test'
+ ...
+# Subtest: out-of-view globe hides the rectangle and zero-longitude bounds wrap on the texture
+ok 369 - out-of-view globe hides the rectangle and zero-longitude bounds wrap on the texture
+ ---
+ duration_ms: 0.127792
+ type: 'test'
+ ...
+# Subtest: system flights keep the viewing angle and approach the center from QMbBW5Yy
+not ok 370 - system flights keep the viewing angle and approach the center from QMbBW5Yy
+ ---
+ duration_ms: 0.777583
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/system-framing.test.mts:31:4'
+ failureType: 'testCodeFailure'
+ error: 'This shared view link uses an unsupported version.'
+ code: 'ERR_TEST_FAILURE'
+ stack: |-
+ parseSharedView (file:///private/tmp/cssearth-pr390/src/renderers/css/dist/chunk-HR5KZMFW.js:70:11)
+ TestContext. (file:///private/tmp/cssearth-pr390/site/test/system-framing.test.mts:32:17)
+ Test.runInAsyncScope (node:async_hooks:214:14)
+ Test.run (node:internal/test_runner/test:1047:25)
+ Test.start (node:internal/test_runner/test:944:17)
+ startSubtestAfterBootstrap (node:internal/test_runner/harness:296:17)
+ ...
+# Subtest: system flights keep the viewing angle and approach the center from QIZBtofG
+not ok 371 - system flights keep the viewing angle and approach the center from QIZBtofG
+ ---
+ duration_ms: 0.129084
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/site/test/system-framing.test.mts:31:4'
+ failureType: 'testCodeFailure'
+ error: 'This shared view link uses an unsupported version.'
+ code: 'ERR_TEST_FAILURE'
+ stack: |-
+ parseSharedView (file:///private/tmp/cssearth-pr390/src/renderers/css/dist/chunk-HR5KZMFW.js:70:11)
+ TestContext. (file:///private/tmp/cssearth-pr390/site/test/system-framing.test.mts:32:17)
+ Test.runInAsyncScope (node:async_hooks:214:14)
+ Test.run (node:internal/test_runner/test:1047:25)
+ Test.processPendingSubtests (node:internal/test_runner/test:744:18)
+ Test.postRun (node:internal/test_runner/test:1173:19)
+ Test.run (node:internal/test_runner/test:1101:12)
+ async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3)
+ ...
+# Subtest: fitting the current angle is independent of prepared box order and member names
+ok 372 - fitting the current angle is independent of prepared box order and member names
+ ---
+ duration_ms: 1.332833
+ type: 'test'
+ ...
+# Subtest: each system fits its complete primary orbits at 1524x1237, offset 0,0
+ok 373 - each system fits its complete primary orbits at 1524x1237, offset 0,0
+ ---
+ duration_ms: 94.4015
+ type: 'test'
+ ...
+# Subtest: each system fits its complete primary orbits at 1280x720, offset 0,0
+ok 374 - each system fits its complete primary orbits at 1280x720, offset 0,0
+ ---
+ duration_ms: 30.942708
+ type: 'test'
+ ...
+# Subtest: each system fits its complete primary orbits at 390x844, offset 0,0
+ok 375 - each system fits its complete primary orbits at 390x844, offset 0,0
+ ---
+ duration_ms: 86.346291
+ type: 'test'
+ ...
+# Subtest: each system fits its complete primary orbits at 1024x700, offset 40,-20
+ok 376 - each system fits its complete primary orbits at 1024x700, offset 40,-20
+ ---
+ duration_ms: 114.753125
+ type: 'test'
+ ...
+# Subtest: another star's system, reached edge-on from the Sun, opens to the shallowest prepared elevation
+ok 377 - another star's system, reached edge-on from the Sun, opens to the shallowest prepared elevation
+ ---
+ duration_ms: 6.2695
+ type: 'test'
+ ...
+# Subtest: selecting the system root pulls back from a planet to all major planets, with a normal close-up on repeat
+ok 378 - selecting the system root pulls back from a planet to all major planets, with a normal close-up on repeat
+ ---
+ duration_ms: 0.647959
+ type: 'test'
+ ...
+# Subtest: system fit leaves clearance for the visible sidebar, header and footer
+ok 379 - system fit leaves clearance for the visible sidebar, header and footer
+ ---
+ duration_ms: 0.383709
+ type: 'test'
+ ...
+# Subtest: adding a small distant moon does not pull the initial camera away from the larger moons
+ok 380 - adding a small distant moon does not pull the initial camera away from the larger moons
+ ---
+ duration_ms: 0.440334
+ type: 'test'
+ ...
+# Subtest: a body without moons uses its normal close-up target on first selection
+ok 381 - a body without moons uses its normal close-up target on first selection
+ ---
+ duration_ms: 0.289958
+ type: 'test'
+ ...
+# Subtest: clicking the already selected body in close-up does not zoom back out to its moons
+ok 382 - clicking the already selected body in close-up does not zoom back out to its moons
+ ---
+ duration_ms: 0.138708
+ type: 'test'
+ ...
+# Subtest: galactic breadcrumbs zoom straight out from the current view without panning or turning
+ok 383 - galactic breadcrumbs zoom straight out from the current view without panning or turning
+ ---
+ duration_ms: 16.247375
+ type: 'test'
+ ...
+# Subtest: a Solar System breadcrumb always restores the system framing from a Sun close-up
+ok 384 - a Solar System breadcrumb always restores the system framing from a Sun close-up
+ ---
+ duration_ms: 0.492333
+ type: 'test'
+ ...
+# Subtest: scene date includes the calendar time and TT scale across calendar days
+ok 385 - scene date includes the calendar time and TT scale across calendar days
+ ---
+ duration_ms: 20.312417
+ type: 'test'
+ ...
+# Subtest: coordinates carry rounded seconds and preserve hemispheres
+ok 386 - coordinates carry rounded seconds and preserve hemispheres
+ ---
+ duration_ms: 0.07675
+ type: 'test'
+ ...
+# Subtest: altitude and distance scale use physical units across camera ranges
+ok 387 - altitude and distance scale use physical units across camera ranges
+ ---
+ duration_ms: 0.495166
+ type: 'test'
+ ...
+# Subtest: surface ruler measures the front surface, not the center plane
+ok 388 - surface ruler measures the front surface, not the center plane
+ ---
+ duration_ms: 0.433959
+ type: 'test'
+ ...
+# Subtest: looking away supplies no invented surface coordinates or behind-camera ruler
+ok 389 - looking away supplies no invented surface coordinates or behind-camera ruler
+ ---
+ duration_ms: 0.11475
+ type: 'test'
+ ...
+# Subtest: objects without prepared geographic axes still have altitude and scale
+ok 390 - objects without prepared geographic axes still have altitude and scale
+ ---
+ duration_ms: 0.099416
+ type: 'test'
+ ...
+# Subtest: galaxy distances keep a finite ruler without a spurious surface hit
+ok 391 - galaxy distances keep a finite ruler without a spurious surface hit
+ ---
+ duration_ms: 0.118833
+ type: 'test'
+ ...
+# Subtest: prepared focus uses its own depth plane for scale and never supplies planetary coordinates
+ok 392 - prepared focus uses its own depth plane for scale and never supplies planetary coordinates
+ ---
+ duration_ms: 0.085417
+ type: 'test'
+ ...
+# Subtest: camera changes coalesce into replaceState while preserving the route, other queries, hash and history state
+ok 393 - camera changes coalesce into replaceState while preserving the route, other queries, hash and history state
+ ---
+ duration_ms: 1.796958
+ type: 'test'
+ ...
+# Subtest: incoming links restore before motion resumes without rewriting their saved playback position
+ok 394 - incoming links restore before motion resumes without rewriting their saved playback position
+ ---
+ duration_ms: 0.419709
+ type: 'test'
+ ...
+# Subtest: obsolete JSON links report an error without restoring or rewriting the current scene
+ok 395 - obsolete JSON links report an error without restoring or rewriting the current scene
+ ---
+ duration_ms: 0.185167
+ type: 'test'
+ ...
+# Subtest: invalid links leave the current scene usable and are replaced only after a camera change
+ok 396 - invalid links leave the current scene usable and are replaced only after a camera change
+ ---
+ duration_ms: 0.351958
+ type: 'test'
+ ...
+# Subtest: popstate restores the stored camera and disposal removes history listeners and scheduled work
+ok 397 - popstate restores the stored camera and disposal removes history listeners and scheduled work
+ ---
+ duration_ms: 0.389333
+ type: 'test'
+ ...
+# Subtest: volume dataset cards preserve all prepared lens previews and explicit attribution
+ok 398 - volume dataset cards preserve all prepared lens previews and explicit attribution
+ ---
+ duration_ms: 1.009667
+ type: 'test'
+ ...
+# Subtest: volume dataset presentation rejects wrong owners, missing lenses and unbound sources
+ok 399 - volume dataset presentation rejects wrong owners, missing lenses and unbound sources
+ ---
+ duration_ms: 0.490541
+ type: 'test'
+ ...
+1..399
+# tests 399
+# suites 0
+# pass 376
+# fail 23
+# cancelled 0
+# skipped 0
+# todo 0
+# duration_ms 19779.742542
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/side-x.png b/src/objects/omega-centauri/evidence/2026-09-20/side-x.png
new file mode 100644
index 0000000000..0506b6c8f1
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-20/side-x.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/side-y.png b/src/objects/omega-centauri/evidence/2026-09-20/side-y.png
new file mode 100644
index 0000000000..bead9cf5c6
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-20/side-y.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/validation.json b/src/objects/omega-centauri/evidence/2026-09-20/validation.json
new file mode 100644
index 0000000000..b4c39a8f18
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-20/validation.json
@@ -0,0 +1,412 @@
+{
+ "schema": "cssearth-omega-app-evidence@1",
+ "recordedAt": "2026-09-20T21:50:32.736Z",
+ "baseRevision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "testedState": "Base revision plus the uncommitted app package, native focus readiness, shared context visibility, scoped provenance tooling and refreshed dependent asset inventories identified below. The base commit alone does not contain this app integration.",
+ "environment": {
+ "node": "v22.23.2",
+ "platform": "darwin",
+ "arch": "arm64",
+ "browser": "148.0.7778.96",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "deviceScaleFactor": 1
+ },
+ "sourcePins": [
+ {
+ "path": ".gitignore",
+ "bytes": 15864,
+ "sha256": "10415a3512410f3367389fc73983c5b2e0019f56fdcd20c37e6fe7febac920bc"
+ },
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "bytes": 13264,
+ "sha256": "67286a0589f42d990d467d5c1e199661bc8b98b7102d152c527f34fc095e4a36"
+ },
+ {
+ "path": "site/test/prepared-context-navigation.test.mts",
+ "bytes": 23828,
+ "sha256": "bc80889dedb3c2b7906dd4792c7be68119442856bae95e4562bbf83c2c2713ba"
+ },
+ {
+ "path": "src/objects/m31/runtime-assets.json",
+ "bytes": 15070,
+ "sha256": "288f180fc548cdcba00f98afecf2db2121cc69a1c7fe9e2e5d736d6662432adb"
+ },
+ {
+ "path": "src/objects/m33/runtime-assets.json",
+ "bytes": 15713,
+ "sha256": "ea94ccdd23f58680df91740f9e7a71c89446eac1533c559d08b434465ea545f0"
+ },
+ {
+ "path": "src/objects/nearby-universe/object.json",
+ "bytes": 456,
+ "sha256": "61ea1293df390647595e3ac471ced4cdedcb17befba635cbc7b00e4be80bbbcf"
+ },
+ {
+ "path": "src/objects/nearby-universe/prepared/manifest.json",
+ "bytes": 355,
+ "sha256": "c4fbba552ace7d685a67116f1db52a8a323545e2678a8c7a74615de602a7fe63"
+ },
+ {
+ "path": "src/objects/nearby-universe/runtime-assets.json",
+ "bytes": 713,
+ "sha256": "ba593972c6a03433669e88931779b3f82218394a4cf53531b733e9bee386f79c"
+ },
+ {
+ "path": "src/objects/omega-centauri/investigations.json",
+ "bytes": 11844,
+ "sha256": "6733ae6b94b38f0eede05c2d4d5adf0b060cb3e25ee9266a70f7a07ba98103e7"
+ },
+ {
+ "path": "src/objects/omega-centauri/object.json",
+ "bytes": 1116,
+ "sha256": "7846b5714f0d3c55f547ab5207914ee7ec1494c82413b650a20a3f4b91242781"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared-assets.json",
+ "bytes": 10599,
+ "sha256": "a307635f2e3b99073e303b62fc7ec43ca731ea9c99a4d47df411c49e921c4fbd"
+ },
+ {
+ "path": "src/objects/omega-centauri/runtime-assets.json",
+ "bytes": 595,
+ "sha256": "e30940cfcec240029ada2ddd666379804e57d863087d97a7ad91be598cb0f5bf"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs.json.gz",
+ "bytes": 2277027,
+ "sha256": "869e9ab24b644c4ea7d04cf2d4e2c719445c205b879eaebf13a420c7d7d75eac"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/01-compiler.json",
+ "bytes": 1361,
+ "sha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json",
+ "bytes": 2508,
+ "sha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/03-observation-structures.json",
+ "bytes": 397,
+ "sha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json",
+ "bytes": 36711,
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json",
+ "bytes": 3842,
+ "sha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/delivery.json",
+ "bytes": 2436,
+ "sha256": "013ec10f387fb5b8ba47ba08412151abce621df4b48b748a68a795c579cbe2aa"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/manifest.json",
+ "bytes": 11285,
+ "sha256": "8048b39285f795d9a06bf824f00b241b7800edfc4f1e13253f750fca4571b569"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/presentation.json",
+ "bytes": 6278,
+ "sha256": "a3e6293031bf6c92c90fd14cc3c9f2329f8bbe7389c443ee6fe8c832d7740859"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/provenance-references.json",
+ "bytes": 1856,
+ "sha256": "9a749e5f63493aa373c47d09b1bfec5bd312c32b6896aafd0d51cb7a29e1d5d4"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/replay-references.json",
+ "bytes": 1302,
+ "sha256": "d289333b8061b55f07f97bad5bef7a515123cc2e5bf845dec0a50724ba6df358"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/request.json",
+ "bytes": 365,
+ "sha256": "043c6218aedd90269ec68822cfa2d729d731b7995150c0e01af8b5c68127e241"
+ },
+ {
+ "path": "src/renderers/css/sky/prepared-sky-runtime.test.ts",
+ "bytes": 52229,
+ "sha256": "3652d5086c4422b64ee66347d6909617693f443e1c7e577452796d5d4a5805e1"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.test.ts",
+ "bytes": 1302,
+ "sha256": "11a9abf0d929d27d84c698e3a1e9684778d65eafe7eb29b39a747e834b999beb"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.ts",
+ "bytes": 873,
+ "sha256": "12a727775a8277fede7cd743642b960a30c10a7334f2e47186d97b6e8d5413f4"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-universe-runtime.ts",
+ "bytes": 48720,
+ "sha256": "fd0d735b8f44cd639e3c9cf5333a4f4fbef1a2c9dbbbf8d288cf59a4441dae2f"
+ },
+ {
+ "path": "src/sources/baumgardt-vasiliev-2021.json",
+ "bytes": 1528,
+ "sha256": "b4ae3323e47295475a036cbe09a6867bb023b8051cda5570eb027b86e7478d9a"
+ },
+ {
+ "path": "src/sources/dsouza-rix-2013.json",
+ "bytes": 1662,
+ "sha256": "bd246d2f836c5ae7d2b60712037d24381b62b408381e81d774cee9080ebff37c"
+ },
+ {
+ "path": "src/sources/eso-eso0844a.json",
+ "bytes": 1855,
+ "sha256": "3a3badbe0e8601a3216ff3ec818b7a216e823a3db6c1bebbbcc619ba81cec237"
+ },
+ {
+ "path": "src/sources/eso-eso1119b.json",
+ "bytes": 1864,
+ "sha256": "19872e1f27077b4525444a15ae4c990fa2ea877daefb92fd1b9408fa24c5c9d6"
+ },
+ {
+ "path": "tests/objects/browser/omega-centauri/native-focus-browser.mts",
+ "bytes": 9389,
+ "sha256": "730b922ea4eb4e500c32d0834a0ace7d816d45a4943d106f9f51132cb77c430d"
+ },
+ {
+ "path": "tests/objects/browser/omega-centauri/promotion-inspection-browser.mts",
+ "bytes": 383,
+ "sha256": "541e88c9d226f3e6ca978556d3486be390e5a8623bf5839efb1dcfa35ef69acc"
+ },
+ {
+ "path": "tests/objects/browser/volume-lens-bank/promotion-inspection.mts",
+ "bytes": 14687,
+ "sha256": "af8599e45889d28d44126c115a7176c451a30baa997e9304d2062ad71768efe9"
+ },
+ {
+ "path": "tools/prepare-volume-provenance.mts",
+ "bytes": 28227,
+ "sha256": "725908ea7e406a82276e82e58bf8d7ed2e830ca345eb1db04a1ab7ff0bc3b862"
+ },
+ {
+ "path": "tools/prepare-volume-provenance.test.mts",
+ "bytes": 26438,
+ "sha256": "0050fec1efc5e7d2ac17d39794023bc8655ecb2648e7e6ea9e213f4de9c31f7c"
+ }
+ ],
+ "browserRuntimePins": [
+ {
+ "path": "src/renderers/css/dist/universe.js",
+ "bytes": 10185,
+ "sha256": "a4aa3371c6836dcb1df973ab5cadbf9c9bd5d3ebb2555e9e46aff31fc2b9c520"
+ },
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ }
+ ],
+ "checks": [
+ {
+ "command": "pnpm build",
+ "result": "passed",
+ "builtPages": 995,
+ "catalogueFragmentPages": 497
+ },
+ {
+ "command": "pnpm test:renderer",
+ "result": "passed",
+ "files": 84,
+ "tests": 594
+ },
+ {
+ "command": "node --test site/test/*.test.mts",
+ "result": "failed",
+ "tests": 399,
+ "passed": 376,
+ "failed": 23,
+ "receipt": "shell-tests.tap",
+ "failureNames": [
+ "15 - every object mounts one canonical high-density image bank",
+ "75 - all objects retain browser evidence for the controls they support",
+ "103 - adding and reordering bodies preserves existing marker bytes",
+ "107 - reproduces the checked-in body images and utility markers",
+ "108 - failed object source preserves accepted files or recoverable backups outside public",
+ "109 - failed late utility source preserves accepted files or recoverable backups outside public",
+ "110 - failed publication preserves accepted files or recoverable backups outside public",
+ "111 - failed rollback preserves accepted files or recoverable backups outside public",
+ "34 - site/test/navigation-router.test.mts",
+ "120 - every actual profile reads its recorded coordinates from the shared camera endpoint",
+ "124 - an already ready object does not depend on a redundant browser waiter",
+ "137 - category browsing and asteroid settings cannot bypass Illustration models",
+ "138 - photographic arrivals use the prepared package camera, excluding modeled and unexposed datasets",
+ "180 - derives deterministic test discovery from an authored object id",
+ "50 - site/test/planet-shell.test.mts",
+ "225 - keeps every planet factsheet comparable and concise",
+ "244 - every object forwards its object-owned legend through the shared shell",
+ "64 - site/test/projective-surface-seam-contract.test.mts",
+ "299 - loads object-owned browser and audit expectations for every implementation",
+ "67 - site/test/router-runtime.test.mts",
+ "68 - site/test/runtime-policy.test.mts",
+ "370 - system flights keep the viewing angle and approach the center from QMbBW5Yy",
+ "371 - system flights keep the viewing angle and approach the center from QIZBtofG"
+ ],
+ "baseline": "Not independently reproduced on current main; no identical-main claim."
+ },
+ {
+ "command": "node --test tools/prepare-volume-provenance.test.mts tools/report-investigations.test.mts tools/nebula-provenance-contract.test.mts src/platform/source-catalog.test.mts tools/object-package-consistency.test.mts",
+ "result": "passed",
+ "tests": 29
+ },
+ {
+ "command": "pnpm typecheck:tests",
+ "result": "passed",
+ "files": 1706
+ },
+ {
+ "command": "pnpm typecheck:renderer; pnpm typecheck:shell; pnpm typecheck:tools; pnpm check:typescript-ownership",
+ "result": "passed",
+ "reuse": "Tool owner unchanged since its recorded app-integration pass; renderer/shell checked after context implementation. Authored JavaScript backlog has no additions."
+ },
+ {
+ "command": "node tests/objects/browser/omega-centauri/native-focus-browser.mts http://127.0.0.1:4219 output/pr390-audit/app-integration/context-native-final",
+ "result": "passed",
+ "receipt": "native-focus.json",
+ "claim": "Cold/warm automatic framing; real drag/wheel/reselection; suppression, real outward restoration and return; one scene/camera; no request/page errors."
+ },
+ {
+ "command": "node tests/objects/browser/omega-centauri/promotion-inspection-browser.mts http://127.0.0.1:4219 output/pr390-audit/app-integration/browser-context-final",
+ "result": "passed",
+ "receipt": "app-inspection.json",
+ "captures": 7,
+ "claim": "Two lens controls and controlled front/oblique/side views in the actual app, separate from native navigation proof."
+ },
+ {
+ "command": "node tools/nebula/prepare.mts --object=omega-centauri (empty-cache independent replay)",
+ "result": "passed",
+ "receipt": "cold-replay.json",
+ "rawResourceIdentities": 1377,
+ "bakedFiles": 60
+ },
+ {
+ "result": "passed",
+ "claim": "Omega cold restore of all 64 pinned R2 bytes and warm reuse without network",
+ "receipt": "r2-restore.json"
+ },
+ {
+ "result": "passed",
+ "claim": "M31/M33/Nearby Universe dependent publication: 200 files cold-restored; four changed keys verified; warm reuse without network",
+ "receipt": "dependent-r2-restore.json"
+ },
+ {
+ "result": "passed",
+ "claim": "Context owner tests and four property-removal mutations; source pins distinguish this from prior focus readiness checks",
+ "receipt": "context-regressions.json"
+ },
+ {
+ "result": "passed",
+ "claim": "Different-vendor final context/transport/browser review with one corrected browser restoration assertion",
+ "receipt": "context-review.json"
+ }
+ ],
+ "captures": [
+ {
+ "path": "src/objects/omega-centauri/evidence/2026-09-20/front.png",
+ "bytes": 361515,
+ "sha256": "15699f9c5573f4b9270b33f12c0ecce680dc8e1cb4a42f363973e145b5d2f595",
+ "sourceCapture": "view-earth",
+ "sourceReceipt": "app-inspection.json"
+ },
+ {
+ "path": "src/objects/omega-centauri/evidence/2026-09-20/oblique.png",
+ "bytes": 385925,
+ "sha256": "e22fbcb65d767e56927a30ab214caad06764a195cc0650845a25f9cc469fa253",
+ "sourceCapture": "view-oblique",
+ "sourceReceipt": "app-inspection.json"
+ },
+ {
+ "path": "src/objects/omega-centauri/evidence/2026-09-20/side-x.png",
+ "bytes": 409805,
+ "sha256": "526cf13e89988b5d33392abd97db842ade6fea989a7c74e665b219481bb02bca",
+ "sourceCapture": "view-side-plus",
+ "sourceReceipt": "app-inspection.json"
+ },
+ {
+ "path": "src/objects/omega-centauri/evidence/2026-09-20/side-y.png",
+ "bytes": 339301,
+ "sha256": "b76500bd18ebbec4020b392f4129ea5d5b73aa4b9926e12cbf44c4f5c12d0640",
+ "sourceCapture": "view-side-y",
+ "sourceReceipt": "app-inspection.json"
+ },
+ {
+ "path": "src/objects/omega-centauri/evidence/2026-09-20/wfi.png",
+ "bytes": 375193,
+ "sha256": "b0c6a4e887c73e704a96d9d54846882f0c37699657a8393ba5231258bb33e7c7",
+ "sourceCapture": "lens-eso0844a",
+ "sourceReceipt": "app-inspection.json"
+ },
+ {
+ "path": "src/objects/omega-centauri/evidence/2026-09-20/native-arrival.png",
+ "bytes": 433050,
+ "sha256": "2dde0d51ef595984fef135f394019cdddccbdf65c01d62b0c44ab80375078c5d",
+ "sourceReceipt": "native-focus.json",
+ "sourceCapture": "ordinary cold native arrival"
+ }
+ ],
+ "captureScope": "Seven controlled captures executed; retain five complementary controlled views plus ordinary native arrival. Duplicate front and reverse-X captures remain local; their metadata remain in app-inspection.json. No visual handoff or scientific acceptance is inferred from capture completion.",
+ "performance": {
+ "before": "performance-baseline.json",
+ "pageOnlyControl": "performance-background-control.json",
+ "after": "performance-after.json",
+ "comparison": "performance-comparison.json",
+ "limitation": "Headless Chromium uses SwiftShader software rendering, not the desktop GPU. Identical start/end poses and input protocol; timed runs were separate from builds/tests."
+ },
+ "dependentPreparation": {
+ "receipt": "nearby-recomputation.json",
+ "meaning": "M31/M33 provenance generator identity changes with the scoped tool; Nearby Universe recomputation changes only tiny floating values and dependent hashes, with unchanged source data/frame/appearance."
+ },
+ "reusedLab": {
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "reason": "No further Lab implementation or fitted-field/baked-bank changes after that revision. App inputs are exact pinned copies; native camera/context results are newly checked rather than inferred from old Lab captures.",
+ "tests": {
+ "total": 681,
+ "passed": 659,
+ "skipped": 17,
+ "failed": 5
+ },
+ "failures": [
+ "Four missing LMC research inputs",
+ "One temporary fixture package-resolution failure"
+ ],
+ "baseline": "Not independently reproduced on current main."
+ },
+ "unresolved": [
+ "Omega neutral and both optical axis-handoff image gates remain failed; earlier scientific bake round cap remains exhausted.",
+ "Minimum zoom remains 0.05 framing radii (2.17 pc), allowing entry into the cluster; close-zoom visual quality not accepted.",
+ "Full broad shell sweep is not green; its 23 failures are retained without calling them an identical-main result.",
+ "Desktop GPU frame rate not measured."
+ ],
+ "mergeStatus": "Draft; not approved for merge or scientific visual acceptance.",
+ "shellTranscript": {
+ "originalBytes": 180336,
+ "originalSha256": "ab9124b128caddc3b86868650879359cea9db84907f36b03d9a5f3f00bfc3168",
+ "normalization": "Removed whitespace on blank lines only; test names, counts, errors and stack traces are unchanged."
+ }
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-20/wfi.png b/src/objects/omega-centauri/evidence/2026-09-20/wfi.png
new file mode 100644
index 0000000000..9d2462bce4
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-20/wfi.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/dependent-r2-restore.json b/src/objects/omega-centauri/evidence/2026-09-21/dependent-r2-restore.json
new file mode 100644
index 0000000000..e614240087
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/dependent-r2-restore.json
@@ -0,0 +1,2382 @@
+{
+ "status": "verified",
+ "revision": "bb7cab3c2e5be0b58a63e2cbeca33ca94a7b3b5a",
+ "comparisonBaseTree": "6bad05ebbfe2ffa39888adef265be5f351216e53",
+ "nearbyMainSourceBindingPreserved": true,
+ "claim": "Standard publication and independent cold byte verification; no source/payload edits or rebake",
+ "root": "/private/tmp/cssearth-pr390/output/pr390-audit/ci-head-bb7cab3/post-merge-publication/cold-final-context-1789942296127",
+ "startedAt": "2026-09-20T22:11:36.179Z",
+ "endedAt": "2026-09-20T22:11:37.725Z",
+ "durationSeconds": 1.545617,
+ "node": "v22.23.2",
+ "platform": "darwin",
+ "arch": "arm64",
+ "inputPins": [
+ {
+ "path": "src/objects/m31/object.json",
+ "sha256": "00adc87856c9653d79e8b1e581bfe0a5f431d3837ef08e496277af7dc2f4d1c5"
+ },
+ {
+ "path": "src/objects/m31/runtime-assets.json",
+ "sha256": "2bd4d877481fd744f0ea58a681ccc6f852ecf182ebeafa3a11d920acd1373b9a"
+ },
+ {
+ "path": "src/objects/m33/object.json",
+ "sha256": "20c22829b5f4a7127c747a5307043addc0d9584bca7671e022247857c0fab211"
+ },
+ {
+ "path": "src/objects/m33/runtime-assets.json",
+ "sha256": "a5a109a5bb110fa7d16c48ecdbe42713414513a40d4418d86ef78f115025c9fa"
+ },
+ {
+ "path": "src/objects/nearby-universe/object.json",
+ "sha256": "61ea1293df390647595e3ac471ced4cdedcb17befba635cbc7b00e4be80bbbcf"
+ },
+ {
+ "path": "src/objects/nearby-universe/runtime-assets.json",
+ "sha256": "d76eabec4d44fd1588021a6b4afdffe175b0ff31659efcc898b3f09e12483957"
+ },
+ {
+ "path": "src/objects/nearby-universe/source/manifest.json",
+ "sha256": "5e3e105e4b1b4f70b4abae418d077c274f23686dec5fbc37e7b8045bed3df924"
+ }
+ ],
+ "sourcePinsUnchanged": true,
+ "publisher": {
+ "selectedObjects": [
+ "m31",
+ "m33",
+ "nearby-universe"
+ ],
+ "assetsChecked": 200,
+ "alreadyPublished": 197,
+ "reportedMissesUploaded": 3,
+ "verified": 200,
+ "logSha256": "1ccbb4912bcd7c62c94fd6389eb8baa32ada355b58e8549496960e4a3d3fd94a",
+ "note": "Publisher checks the whole selected closure. Independent cold GET verification proves every changed key live with exact bytes; earlier publication receipts are superseded for these changed pins."
+ },
+ "changedKeys": [
+ {
+ "id": "m31",
+ "filename": "provenance.json",
+ "key": "runtime-assets/d96140b77831ccd093d0da69a5cf4659ddc53d4e11d2c686d8216854a13cdef1/provenance.json",
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d96140b77831ccd093d0da69a5cf4659ddc53d4e11d2c686d8216854a13cdef1/provenance.json",
+ "bytes": 38541,
+ "sha256": "d96140b77831ccd093d0da69a5cf4659ddc53d4e11d2c686d8216854a13cdef1",
+ "previous": {
+ "sha256": "8fd017ce96e753da0d6fd53ed40040705e56c1a436605d6cd90bdecd98212404",
+ "bytes": 38541
+ },
+ "httpStatus": 200
+ },
+ {
+ "id": "m33",
+ "filename": "provenance.json",
+ "key": "runtime-assets/2db0a9aa6db465c0eb759ffa694dc4c68f8df98f8e68ac5fb7913e8f0426785d/provenance.json",
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2db0a9aa6db465c0eb759ffa694dc4c68f8df98f8e68ac5fb7913e8f0426785d/provenance.json",
+ "bytes": 37975,
+ "sha256": "2db0a9aa6db465c0eb759ffa694dc4c68f8df98f8e68ac5fb7913e8f0426785d",
+ "previous": {
+ "sha256": "24e8fb2b2e5a7d2ed514543b7f13f76b066a2eb859acf6ee54ab6df0ddb78981",
+ "bytes": 37975
+ },
+ "httpStatus": 200
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "points.json",
+ "key": "runtime-assets/595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee/points.json",
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee/points.json",
+ "bytes": 345406,
+ "sha256": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee",
+ "previous": {
+ "sha256": "c70262ba6e3ae288da6acd29971d1b8da0eeb33ae8a924fcfa39ff9b2eceb8d3",
+ "bytes": 345401
+ },
+ "httpStatus": 200
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "provenance.json",
+ "key": "runtime-assets/d212db52a17a691903764ee360ea3cd30427b7290771f7a4581b00de1ce61b5d/provenance.json",
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d212db52a17a691903764ee360ea3cd30427b7290771f7a4581b00de1ce61b5d/provenance.json",
+ "bytes": 10354,
+ "sha256": "d212db52a17a691903764ee360ea3cd30427b7290771f7a4581b00de1ce61b5d",
+ "previous": {
+ "sha256": "00dcf2448fb75c786fca834abb09d7bbdfc4dbd6cb9d235f456b0c65aad04a55",
+ "bytes": 10354
+ },
+ "httpStatus": 200
+ }
+ ],
+ "unchangedAssetCount": 196,
+ "unchangedExamples": [
+ {
+ "id": "m31",
+ "filename": "presentation.json",
+ "sha256": "ef01151d319c4a1ad480d65a58cafaf24db1b3d28ac5deab5bcb0a420d36dbe1",
+ "bytes": 1857
+ },
+ {
+ "id": "m33",
+ "filename": "presentation.json",
+ "sha256": "9193e89bf84c577cf77f8fb4d584505b03a069264f009959ece9df73c6c59d71",
+ "bytes": 1799
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "cloud.webp",
+ "sha256": "ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c",
+ "bytes": 1706
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "presentation.json",
+ "sha256": "224e620caa689a95820bb15ad87bc37e7c7a6c2489db2136a441096246b10889",
+ "bytes": 490
+ }
+ ],
+ "restored": {
+ "installed": 200,
+ "reused": 0,
+ "skipped": 0
+ },
+ "repeat": {
+ "installed": 0,
+ "reused": 200,
+ "skipped": 0
+ },
+ "descriptors": [
+ {
+ "id": "m31",
+ "path": "prepared/image-layers.json",
+ "sha256": "16951a0b2f8fcccdd5c9b7c419cd6e486fda759448437ffe06fc6ba20a3b3957"
+ },
+ {
+ "id": "m33",
+ "path": "prepared/image-layers.json",
+ "sha256": "1e89f7d7ea9673eeebbbc3ad6da2695202dce3dfd2281830aa5fa5ff1c9e6572"
+ },
+ {
+ "id": "nearby-universe",
+ "path": "prepared/points.json",
+ "sha256": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee"
+ }
+ ],
+ "manifestBinding": [
+ {
+ "filename": "points.json",
+ "sha256": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee",
+ "bytes": 345406
+ },
+ {
+ "filename": "cloud.webp",
+ "sha256": "ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c",
+ "bytes": 1706
+ }
+ ],
+ "verified": [
+ {
+ "id": "m31",
+ "filename": "image-layers.json",
+ "bytes": 177609,
+ "sha256": "16951a0b2f8fcccdd5c9b7c419cd6e486fda759448437ffe06fc6ba20a3b3957",
+ "key": "runtime-assets/16951a0b2f8fcccdd5c9b7c419cd6e486fda759448437ffe06fc6ba20a3b3957/image-layers.json"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-00.webp",
+ "bytes": 1492,
+ "sha256": "6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353",
+ "key": "runtime-assets/6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353/layers/z-00.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-01.webp",
+ "bytes": 2038,
+ "sha256": "db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596",
+ "key": "runtime-assets/db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596/layers/z-01.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-02.webp",
+ "bytes": 2690,
+ "sha256": "7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05",
+ "key": "runtime-assets/7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05/layers/z-02.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-03.webp",
+ "bytes": 3726,
+ "sha256": "4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01",
+ "key": "runtime-assets/4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01/layers/z-03.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-04.webp",
+ "bytes": 4344,
+ "sha256": "7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4",
+ "key": "runtime-assets/7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4/layers/z-04.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-05.webp",
+ "bytes": 4898,
+ "sha256": "421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35",
+ "key": "runtime-assets/421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35/layers/z-05.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-06.webp",
+ "bytes": 5830,
+ "sha256": "567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137",
+ "key": "runtime-assets/567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137/layers/z-06.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-07.webp",
+ "bytes": 6744,
+ "sha256": "118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35",
+ "key": "runtime-assets/118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35/layers/z-07.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-08.webp",
+ "bytes": 7838,
+ "sha256": "77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4",
+ "key": "runtime-assets/77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4/layers/z-08.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-09.webp",
+ "bytes": 8660,
+ "sha256": "3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f",
+ "key": "runtime-assets/3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f/layers/z-09.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-10.webp",
+ "bytes": 9446,
+ "sha256": "9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93",
+ "key": "runtime-assets/9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93/layers/z-10.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-11.webp",
+ "bytes": 10042,
+ "sha256": "c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3",
+ "key": "runtime-assets/c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3/layers/z-11.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-12.webp",
+ "bytes": 10718,
+ "sha256": "b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572",
+ "key": "runtime-assets/b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572/layers/z-12.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-13.webp",
+ "bytes": 11342,
+ "sha256": "cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911",
+ "key": "runtime-assets/cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911/layers/z-13.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-14.webp",
+ "bytes": 11474,
+ "sha256": "d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433",
+ "key": "runtime-assets/d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433/layers/z-14.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-15.webp",
+ "bytes": 11362,
+ "sha256": "b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284",
+ "key": "runtime-assets/b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284/layers/z-15.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-16.webp",
+ "bytes": 11362,
+ "sha256": "b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284",
+ "key": "runtime-assets/b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284/layers/z-16.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-17.webp",
+ "bytes": 11474,
+ "sha256": "d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433",
+ "key": "runtime-assets/d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433/layers/z-17.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-18.webp",
+ "bytes": 11342,
+ "sha256": "cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911",
+ "key": "runtime-assets/cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911/layers/z-18.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-19.webp",
+ "bytes": 10718,
+ "sha256": "b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572",
+ "key": "runtime-assets/b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572/layers/z-19.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-20.webp",
+ "bytes": 10042,
+ "sha256": "c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3",
+ "key": "runtime-assets/c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3/layers/z-20.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-21.webp",
+ "bytes": 9446,
+ "sha256": "9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93",
+ "key": "runtime-assets/9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93/layers/z-21.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-22.webp",
+ "bytes": 8660,
+ "sha256": "3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f",
+ "key": "runtime-assets/3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f/layers/z-22.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-23.webp",
+ "bytes": 7838,
+ "sha256": "77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4",
+ "key": "runtime-assets/77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4/layers/z-23.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-24.webp",
+ "bytes": 6744,
+ "sha256": "118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35",
+ "key": "runtime-assets/118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35/layers/z-24.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-25.webp",
+ "bytes": 5830,
+ "sha256": "567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137",
+ "key": "runtime-assets/567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137/layers/z-25.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-26.webp",
+ "bytes": 4898,
+ "sha256": "421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35",
+ "key": "runtime-assets/421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35/layers/z-26.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-27.webp",
+ "bytes": 4344,
+ "sha256": "7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4",
+ "key": "runtime-assets/7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4/layers/z-27.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-28.webp",
+ "bytes": 3726,
+ "sha256": "4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01",
+ "key": "runtime-assets/4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01/layers/z-28.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-29.webp",
+ "bytes": 2690,
+ "sha256": "7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05",
+ "key": "runtime-assets/7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05/layers/z-29.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-30.webp",
+ "bytes": 2038,
+ "sha256": "db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596",
+ "key": "runtime-assets/db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596/layers/z-30.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-31.webp",
+ "bytes": 1492,
+ "sha256": "6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353",
+ "key": "runtime-assets/6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353/layers/z-31.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/z-detail.webp",
+ "bytes": 3247660,
+ "sha256": "7da54ef71c1872bf2da0180a62f5d516a8f524b56da9405c3bf8c5a1c6b0009c",
+ "key": "runtime-assets/7da54ef71c1872bf2da0180a62f5d516a8f524b56da9405c3bf8c5a1c6b0009c/layers/z-detail.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-01.webp",
+ "bytes": 548,
+ "sha256": "e78eeeb4e4bff12c71779eb59ad753e333a202b46a8e6482932b8379509f0f5c",
+ "key": "runtime-assets/e78eeeb4e4bff12c71779eb59ad753e333a202b46a8e6482932b8379509f0f5c/layers/x-01.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-02.webp",
+ "bytes": 966,
+ "sha256": "d464be75e453dc4a95e26c7fb38c5f46e6a7a0eb37497dad32c3cfe0db95938f",
+ "key": "runtime-assets/d464be75e453dc4a95e26c7fb38c5f46e6a7a0eb37497dad32c3cfe0db95938f/layers/x-02.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-03.webp",
+ "bytes": 1312,
+ "sha256": "a973db4172b7d63477c0ca311ddd238f7f13fa3bbc6a1bdb8f4b1e0b9b11896a",
+ "key": "runtime-assets/a973db4172b7d63477c0ca311ddd238f7f13fa3bbc6a1bdb8f4b1e0b9b11896a/layers/x-03.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-04.webp",
+ "bytes": 1678,
+ "sha256": "ab3c45ef714a5e9e051f58cd1089d4c5d0d83c93d0ef5db193caab6e1a445d6e",
+ "key": "runtime-assets/ab3c45ef714a5e9e051f58cd1089d4c5d0d83c93d0ef5db193caab6e1a445d6e/layers/x-04.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-05.webp",
+ "bytes": 1600,
+ "sha256": "d46f27c04493425a415a5d39e2a9e9acc2eb2f9b04682e3db83fad6dfe638e49",
+ "key": "runtime-assets/d46f27c04493425a415a5d39e2a9e9acc2eb2f9b04682e3db83fad6dfe638e49/layers/x-05.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-06.webp",
+ "bytes": 1634,
+ "sha256": "01f47abf55059be7fd2647523fc87600e2d63834f8c7e70640b5711c15638c9b",
+ "key": "runtime-assets/01f47abf55059be7fd2647523fc87600e2d63834f8c7e70640b5711c15638c9b/layers/x-06.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-07.webp",
+ "bytes": 1768,
+ "sha256": "2de4319c85aaab323cedaeedfc86bdab53db0bfc33019e6eeb3d8ccca2ae97f1",
+ "key": "runtime-assets/2de4319c85aaab323cedaeedfc86bdab53db0bfc33019e6eeb3d8ccca2ae97f1/layers/x-07.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-08.webp",
+ "bytes": 1836,
+ "sha256": "e71c5a2506fdceedb650dd2babc5e73cc69fe4beea48278782749fd1840e5a6d",
+ "key": "runtime-assets/e71c5a2506fdceedb650dd2babc5e73cc69fe4beea48278782749fd1840e5a6d/layers/x-08.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-09.webp",
+ "bytes": 1984,
+ "sha256": "6da86dca8873f6d5bad23a049050ad14b0bb186484dba9d4e5f8601f95d9dcf6",
+ "key": "runtime-assets/6da86dca8873f6d5bad23a049050ad14b0bb186484dba9d4e5f8601f95d9dcf6/layers/x-09.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-10.webp",
+ "bytes": 2026,
+ "sha256": "eb854b16b3824e8e47f87f879e56808868f689e036cd4483dd01bd78457f83a8",
+ "key": "runtime-assets/eb854b16b3824e8e47f87f879e56808868f689e036cd4483dd01bd78457f83a8/layers/x-10.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-11.webp",
+ "bytes": 2448,
+ "sha256": "066d7e6d9b2057384cca5e78af641df790f5ccabdee90fe68b0ac793e3714267",
+ "key": "runtime-assets/066d7e6d9b2057384cca5e78af641df790f5ccabdee90fe68b0ac793e3714267/layers/x-11.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-12.webp",
+ "bytes": 2404,
+ "sha256": "f094f5781ece11c3306c3343bcf0267b3a77ba83f69aa4045ac222bcca5863c5",
+ "key": "runtime-assets/f094f5781ece11c3306c3343bcf0267b3a77ba83f69aa4045ac222bcca5863c5/layers/x-12.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-13.webp",
+ "bytes": 1948,
+ "sha256": "e55df8e13829273a44e7c03d2d210d3ee8ac8a444f3e4b90a52d497427260412",
+ "key": "runtime-assets/e55df8e13829273a44e7c03d2d210d3ee8ac8a444f3e4b90a52d497427260412/layers/x-13.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-14.webp",
+ "bytes": 2264,
+ "sha256": "add47432f770ba703f598efb70a50dac12af9d03ead2de75d3c1e9c878f91d30",
+ "key": "runtime-assets/add47432f770ba703f598efb70a50dac12af9d03ead2de75d3c1e9c878f91d30/layers/x-14.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-15.webp",
+ "bytes": 2338,
+ "sha256": "900b1aada73e64f398e8a0245f28ae7bf651740879f64c71f18ac604293e407d",
+ "key": "runtime-assets/900b1aada73e64f398e8a0245f28ae7bf651740879f64c71f18ac604293e407d/layers/x-15.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-16.webp",
+ "bytes": 1958,
+ "sha256": "3fae9a41364c44e2687c51fd3fd215a5af3ab59f46dce8b0a7fa1d8e80f1e76f",
+ "key": "runtime-assets/3fae9a41364c44e2687c51fd3fd215a5af3ab59f46dce8b0a7fa1d8e80f1e76f/layers/x-16.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-17.webp",
+ "bytes": 1864,
+ "sha256": "262b930cf8a41787c65a3b2f4089106f5a9d379e122f56067d4239a84d65e61e",
+ "key": "runtime-assets/262b930cf8a41787c65a3b2f4089106f5a9d379e122f56067d4239a84d65e61e/layers/x-17.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-18.webp",
+ "bytes": 2090,
+ "sha256": "886d478112b4f95449d46d239d253649c8c54ac652349ca8ddbed7436ac80a61",
+ "key": "runtime-assets/886d478112b4f95449d46d239d253649c8c54ac652349ca8ddbed7436ac80a61/layers/x-18.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-19.webp",
+ "bytes": 1684,
+ "sha256": "15390b6c4c5c8cfd19c85857df4b9aaf80f0dd701c62ef665ad75c445cfd00ea",
+ "key": "runtime-assets/15390b6c4c5c8cfd19c85857df4b9aaf80f0dd701c62ef665ad75c445cfd00ea/layers/x-19.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-20.webp",
+ "bytes": 1830,
+ "sha256": "d4741e12d20490cc650b95df8c8b7e1ff2615f148be0a759592bd7684b566729",
+ "key": "runtime-assets/d4741e12d20490cc650b95df8c8b7e1ff2615f148be0a759592bd7684b566729/layers/x-20.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-21.webp",
+ "bytes": 1722,
+ "sha256": "14fd79cc7ad12c1d9f7f9e730c0a5d928e8b4eede72216c4074a15b778ef45df",
+ "key": "runtime-assets/14fd79cc7ad12c1d9f7f9e730c0a5d928e8b4eede72216c4074a15b778ef45df/layers/x-21.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-22.webp",
+ "bytes": 1582,
+ "sha256": "8806454f09e05a8242169fb729ffd62353ad6dec22e6372e20936ed6cd9afcb8",
+ "key": "runtime-assets/8806454f09e05a8242169fb729ffd62353ad6dec22e6372e20936ed6cd9afcb8/layers/x-22.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-23.webp",
+ "bytes": 1458,
+ "sha256": "7229f90e841349a0e3040a7ba36cb8e6a6fd031786ba276d652e9966cca9b352",
+ "key": "runtime-assets/7229f90e841349a0e3040a7ba36cb8e6a6fd031786ba276d652e9966cca9b352/layers/x-23.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-24.webp",
+ "bytes": 1348,
+ "sha256": "012fa982feec9ee649988eefe9d359394c6266c67397c086afbc7bfecde4a3ea",
+ "key": "runtime-assets/012fa982feec9ee649988eefe9d359394c6266c67397c086afbc7bfecde4a3ea/layers/x-24.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-25.webp",
+ "bytes": 942,
+ "sha256": "5275337df162438547ce51109b8825bc30c08a4b58e4e982945f343643d91058",
+ "key": "runtime-assets/5275337df162438547ce51109b8825bc30c08a4b58e4e982945f343643d91058/layers/x-25.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-26.webp",
+ "bytes": 662,
+ "sha256": "aaf60d06e5b0a7b9136f9aac1a518c1bf59c72b2d7734f5f4b74305a3c3bc3ea",
+ "key": "runtime-assets/aaf60d06e5b0a7b9136f9aac1a518c1bf59c72b2d7734f5f4b74305a3c3bc3ea/layers/x-26.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-27.webp",
+ "bytes": 398,
+ "sha256": "1cb5d9e6b724c19e5800d8fe1a093cce2ed89146dff460952b703374491e43ee",
+ "key": "runtime-assets/1cb5d9e6b724c19e5800d8fe1a093cce2ed89146dff460952b703374491e43ee/layers/x-27.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/x-28.webp",
+ "bytes": 112,
+ "sha256": "7be4374bf82ad267a3412e7c318c13e86c03b6160fb7d3ad1ef1f10800af2bde",
+ "key": "runtime-assets/7be4374bf82ad267a3412e7c318c13e86c03b6160fb7d3ad1ef1f10800af2bde/layers/x-28.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-00.webp",
+ "bytes": 118,
+ "sha256": "c0ecd45f8bce89e8bcb5b6b2b419e8d4bb2ce63967154a7a3cfe9a854e8a30c8",
+ "key": "runtime-assets/c0ecd45f8bce89e8bcb5b6b2b419e8d4bb2ce63967154a7a3cfe9a854e8a30c8/layers/y-00.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-01.webp",
+ "bytes": 516,
+ "sha256": "d369ea41b48f54cde8701fa2ca046a1500658967a22e472af9671d5f07a611a9",
+ "key": "runtime-assets/d369ea41b48f54cde8701fa2ca046a1500658967a22e472af9671d5f07a611a9/layers/y-01.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-02.webp",
+ "bytes": 692,
+ "sha256": "670683e45cc76f09d2fabaa8c33d0de4f985e6a3e408dafbc9f42853c450ebae",
+ "key": "runtime-assets/670683e45cc76f09d2fabaa8c33d0de4f985e6a3e408dafbc9f42853c450ebae/layers/y-02.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-03.webp",
+ "bytes": 942,
+ "sha256": "ef21f88b9a5a3fe6caf0f5eb77dec87fa0e9fa776ef208ab3177f75b83d779ef",
+ "key": "runtime-assets/ef21f88b9a5a3fe6caf0f5eb77dec87fa0e9fa776ef208ab3177f75b83d779ef/layers/y-03.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-04.webp",
+ "bytes": 1392,
+ "sha256": "1877add88dec4803e86ad04981d6a616125cbb4a9b23828e10661ac0df3a29a4",
+ "key": "runtime-assets/1877add88dec4803e86ad04981d6a616125cbb4a9b23828e10661ac0df3a29a4/layers/y-04.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-05.webp",
+ "bytes": 1452,
+ "sha256": "ad8c4648010f0facc726cf6f64de2421045c05b50756876960bf550f6168e02e",
+ "key": "runtime-assets/ad8c4648010f0facc726cf6f64de2421045c05b50756876960bf550f6168e02e/layers/y-05.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-06.webp",
+ "bytes": 1398,
+ "sha256": "872d95de54845c3768c0e638de78f8f43c6fc1f9bc233988bea64d53ca86d821",
+ "key": "runtime-assets/872d95de54845c3768c0e638de78f8f43c6fc1f9bc233988bea64d53ca86d821/layers/y-06.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-07.webp",
+ "bytes": 1460,
+ "sha256": "b3129189d8e02ba191f73fdd6ef0f74a6fad646cabbc7df354205e8438b90b57",
+ "key": "runtime-assets/b3129189d8e02ba191f73fdd6ef0f74a6fad646cabbc7df354205e8438b90b57/layers/y-07.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-08.webp",
+ "bytes": 1540,
+ "sha256": "ec870ddaeaa7615f5d8ab0f5d46a78d320aaf9fc59b806e2ffa1df85a91057ec",
+ "key": "runtime-assets/ec870ddaeaa7615f5d8ab0f5d46a78d320aaf9fc59b806e2ffa1df85a91057ec/layers/y-08.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-09.webp",
+ "bytes": 1626,
+ "sha256": "c288ad201a5644b8521a5d9a20a67479b89d2bcd24354ab17b9f4ef67d845db3",
+ "key": "runtime-assets/c288ad201a5644b8521a5d9a20a67479b89d2bcd24354ab17b9f4ef67d845db3/layers/y-09.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-10.webp",
+ "bytes": 1628,
+ "sha256": "1d9665c4314ff93e641de2368f9ea887d2fe6f81c570a7f3a3606d4a952084e4",
+ "key": "runtime-assets/1d9665c4314ff93e641de2368f9ea887d2fe6f81c570a7f3a3606d4a952084e4/layers/y-10.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-11.webp",
+ "bytes": 1474,
+ "sha256": "cfb523ee09f4f5a755910c5b86f3b8227ef46f6035eb2d5493eedcc84583c098",
+ "key": "runtime-assets/cfb523ee09f4f5a755910c5b86f3b8227ef46f6035eb2d5493eedcc84583c098/layers/y-11.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-12.webp",
+ "bytes": 1758,
+ "sha256": "dfafcb4e68bac5394285b3d7ea589ff43444f3732d76db17d363e59fa694d454",
+ "key": "runtime-assets/dfafcb4e68bac5394285b3d7ea589ff43444f3732d76db17d363e59fa694d454/layers/y-12.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-13.webp",
+ "bytes": 1736,
+ "sha256": "455eb90107ff596122362fb42d9f979f55a65734e9290eefd3a2888155999ce4",
+ "key": "runtime-assets/455eb90107ff596122362fb42d9f979f55a65734e9290eefd3a2888155999ce4/layers/y-13.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-14.webp",
+ "bytes": 2118,
+ "sha256": "334b0a6ce53fcdeff82133aeabba8286dbe71e1463915b9c8af55663b60170dc",
+ "key": "runtime-assets/334b0a6ce53fcdeff82133aeabba8286dbe71e1463915b9c8af55663b60170dc/layers/y-14.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-15.webp",
+ "bytes": 2204,
+ "sha256": "3f368bbbe73ca3a4a9fdcf6aecd1413aee471aaf8eba6da51df7e4c7d997fe1f",
+ "key": "runtime-assets/3f368bbbe73ca3a4a9fdcf6aecd1413aee471aaf8eba6da51df7e4c7d997fe1f/layers/y-15.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-16.webp",
+ "bytes": 1836,
+ "sha256": "c895666cb91769fca707167be46d0ba6f3285e295aa0cf06afcb734bf775b3be",
+ "key": "runtime-assets/c895666cb91769fca707167be46d0ba6f3285e295aa0cf06afcb734bf775b3be/layers/y-16.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-17.webp",
+ "bytes": 2068,
+ "sha256": "c3d435478a2f011e0f09d0a99293cfcdb88b57ffbb1613635999478bf4b50bbf",
+ "key": "runtime-assets/c3d435478a2f011e0f09d0a99293cfcdb88b57ffbb1613635999478bf4b50bbf/layers/y-17.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-18.webp",
+ "bytes": 1768,
+ "sha256": "69e82473c09d985ee2b9d163c9ff249f0bca837b0b47834575435e2b05edcf6f",
+ "key": "runtime-assets/69e82473c09d985ee2b9d163c9ff249f0bca837b0b47834575435e2b05edcf6f/layers/y-18.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-19.webp",
+ "bytes": 1720,
+ "sha256": "0ca061c1e4cb504647e15696f74fde84b5f3a9685f62562a0342536c2ca724b4",
+ "key": "runtime-assets/0ca061c1e4cb504647e15696f74fde84b5f3a9685f62562a0342536c2ca724b4/layers/y-19.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-20.webp",
+ "bytes": 1766,
+ "sha256": "040001269ace310d7afc32e4dffb240878009d3d033b64b399c0c49c720bb2f6",
+ "key": "runtime-assets/040001269ace310d7afc32e4dffb240878009d3d033b64b399c0c49c720bb2f6/layers/y-20.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-21.webp",
+ "bytes": 1548,
+ "sha256": "7f427707bf01b3682239f90b165a3811617fd97dfab59b4f18903402a643b615",
+ "key": "runtime-assets/7f427707bf01b3682239f90b165a3811617fd97dfab59b4f18903402a643b615/layers/y-21.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-22.webp",
+ "bytes": 1750,
+ "sha256": "ced11e3d3bd1f5ef96646ce2f7b7f7e8f54047e57398fda77c8d24146659b429",
+ "key": "runtime-assets/ced11e3d3bd1f5ef96646ce2f7b7f7e8f54047e57398fda77c8d24146659b429/layers/y-22.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-23.webp",
+ "bytes": 1636,
+ "sha256": "1ab16b4007a25fcc92b58788f7be38c3e928e5263bf940f5d3e183cab2d88a57",
+ "key": "runtime-assets/1ab16b4007a25fcc92b58788f7be38c3e928e5263bf940f5d3e183cab2d88a57/layers/y-23.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-24.webp",
+ "bytes": 1586,
+ "sha256": "262b3347921ae8a48a60a2395616aeeeca477312bdc8e4343d9531b392b8c711",
+ "key": "runtime-assets/262b3347921ae8a48a60a2395616aeeeca477312bdc8e4343d9531b392b8c711/layers/y-24.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-25.webp",
+ "bytes": 1684,
+ "sha256": "e198fb0a565b3402bf3789a166d618cd645f736600158378d2ca73e112300976",
+ "key": "runtime-assets/e198fb0a565b3402bf3789a166d618cd645f736600158378d2ca73e112300976/layers/y-25.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-26.webp",
+ "bytes": 1392,
+ "sha256": "bdb46824a992a8cd31c9c91cd6bc0987d390359762f70eb12c194d17288985f2",
+ "key": "runtime-assets/bdb46824a992a8cd31c9c91cd6bc0987d390359762f70eb12c194d17288985f2/layers/y-26.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-27.webp",
+ "bytes": 1220,
+ "sha256": "0764e92999e6a776fd15124558847e763a0bf585e3fe33fa36fe90ddaf7c27ed",
+ "key": "runtime-assets/0764e92999e6a776fd15124558847e763a0bf585e3fe33fa36fe90ddaf7c27ed/layers/y-27.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-28.webp",
+ "bytes": 1072,
+ "sha256": "ad902a30624a2659ab0e680739684698a3e1348305e6ee0920f879dd9a04fdb7",
+ "key": "runtime-assets/ad902a30624a2659ab0e680739684698a3e1348305e6ee0920f879dd9a04fdb7/layers/y-28.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-29.webp",
+ "bytes": 856,
+ "sha256": "60da9e0b67cf85fbec7fbab7530827fdc874a0443e225acfd9d21ba7e98357ab",
+ "key": "runtime-assets/60da9e0b67cf85fbec7fbab7530827fdc874a0443e225acfd9d21ba7e98357ab/layers/y-29.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "layers/y-30.webp",
+ "bytes": 726,
+ "sha256": "61303a62465dd706e6f1ddb23dba2c16c89d0493e67c16b67f38e890242cb4f3",
+ "key": "runtime-assets/61303a62465dd706e6f1ddb23dba2c16c89d0493e67c16b67f38e890242cb4f3/layers/y-30.webp"
+ },
+ {
+ "id": "m31",
+ "filename": "provenance.json",
+ "bytes": 38541,
+ "sha256": "d96140b77831ccd093d0da69a5cf4659ddc53d4e11d2c686d8216854a13cdef1",
+ "key": "runtime-assets/d96140b77831ccd093d0da69a5cf4659ddc53d4e11d2c686d8216854a13cdef1/provenance.json"
+ },
+ {
+ "id": "m31",
+ "filename": "presentation.json",
+ "bytes": 1857,
+ "sha256": "ef01151d319c4a1ad480d65a58cafaf24db1b3d28ac5deab5bcb0a420d36dbe1",
+ "key": "runtime-assets/ef01151d319c4a1ad480d65a58cafaf24db1b3d28ac5deab5bcb0a420d36dbe1/presentation.json"
+ },
+ {
+ "id": "m31",
+ "filename": "datasets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9.webp",
+ "bytes": 85990,
+ "sha256": "4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9",
+ "key": "runtime-assets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9/datasets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "image-layers.json",
+ "bytes": 183493,
+ "sha256": "1e89f7d7ea9673eeebbbc3ad6da2695202dce3dfd2281830aa5fa5ff1c9e6572",
+ "key": "runtime-assets/1e89f7d7ea9673eeebbbc3ad6da2695202dce3dfd2281830aa5fa5ff1c9e6572/image-layers.json"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-00.webp",
+ "bytes": 4654,
+ "sha256": "d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe",
+ "key": "runtime-assets/d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe/layers/z-00.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-01.webp",
+ "bytes": 6346,
+ "sha256": "5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd",
+ "key": "runtime-assets/5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd/layers/z-01.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-02.webp",
+ "bytes": 7994,
+ "sha256": "cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f",
+ "key": "runtime-assets/cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f/layers/z-02.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-03.webp",
+ "bytes": 9694,
+ "sha256": "e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8",
+ "key": "runtime-assets/e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8/layers/z-03.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-04.webp",
+ "bytes": 11696,
+ "sha256": "4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb",
+ "key": "runtime-assets/4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb/layers/z-04.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-05.webp",
+ "bytes": 12886,
+ "sha256": "90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c",
+ "key": "runtime-assets/90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c/layers/z-05.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-06.webp",
+ "bytes": 15030,
+ "sha256": "d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4",
+ "key": "runtime-assets/d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4/layers/z-06.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-07.webp",
+ "bytes": 17242,
+ "sha256": "476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d",
+ "key": "runtime-assets/476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d/layers/z-07.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-08.webp",
+ "bytes": 19274,
+ "sha256": "4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67",
+ "key": "runtime-assets/4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67/layers/z-08.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-09.webp",
+ "bytes": 21476,
+ "sha256": "ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e",
+ "key": "runtime-assets/ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e/layers/z-09.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-10.webp",
+ "bytes": 22888,
+ "sha256": "6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b",
+ "key": "runtime-assets/6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b/layers/z-10.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-11.webp",
+ "bytes": 24308,
+ "sha256": "69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee",
+ "key": "runtime-assets/69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee/layers/z-11.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-12.webp",
+ "bytes": 26204,
+ "sha256": "cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b",
+ "key": "runtime-assets/cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b/layers/z-12.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-13.webp",
+ "bytes": 27288,
+ "sha256": "7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac",
+ "key": "runtime-assets/7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac/layers/z-13.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-14.webp",
+ "bytes": 27622,
+ "sha256": "4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8",
+ "key": "runtime-assets/4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8/layers/z-14.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-15.webp",
+ "bytes": 27546,
+ "sha256": "c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3",
+ "key": "runtime-assets/c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3/layers/z-15.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-16.webp",
+ "bytes": 27546,
+ "sha256": "c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3",
+ "key": "runtime-assets/c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3/layers/z-16.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-17.webp",
+ "bytes": 27622,
+ "sha256": "4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8",
+ "key": "runtime-assets/4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8/layers/z-17.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-18.webp",
+ "bytes": 27288,
+ "sha256": "7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac",
+ "key": "runtime-assets/7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac/layers/z-18.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-19.webp",
+ "bytes": 26204,
+ "sha256": "cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b",
+ "key": "runtime-assets/cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b/layers/z-19.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-20.webp",
+ "bytes": 24308,
+ "sha256": "69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee",
+ "key": "runtime-assets/69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee/layers/z-20.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-21.webp",
+ "bytes": 22888,
+ "sha256": "6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b",
+ "key": "runtime-assets/6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b/layers/z-21.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-22.webp",
+ "bytes": 21476,
+ "sha256": "ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e",
+ "key": "runtime-assets/ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e/layers/z-22.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-23.webp",
+ "bytes": 19274,
+ "sha256": "4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67",
+ "key": "runtime-assets/4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67/layers/z-23.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-24.webp",
+ "bytes": 17242,
+ "sha256": "476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d",
+ "key": "runtime-assets/476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d/layers/z-24.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-25.webp",
+ "bytes": 15030,
+ "sha256": "d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4",
+ "key": "runtime-assets/d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4/layers/z-25.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-26.webp",
+ "bytes": 12886,
+ "sha256": "90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c",
+ "key": "runtime-assets/90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c/layers/z-26.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-27.webp",
+ "bytes": 11696,
+ "sha256": "4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb",
+ "key": "runtime-assets/4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb/layers/z-27.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-28.webp",
+ "bytes": 9694,
+ "sha256": "e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8",
+ "key": "runtime-assets/e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8/layers/z-28.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-29.webp",
+ "bytes": 7994,
+ "sha256": "cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f",
+ "key": "runtime-assets/cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f/layers/z-29.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-30.webp",
+ "bytes": 6346,
+ "sha256": "5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd",
+ "key": "runtime-assets/5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd/layers/z-30.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-31.webp",
+ "bytes": 4654,
+ "sha256": "d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe",
+ "key": "runtime-assets/d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe/layers/z-31.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/z-detail.webp",
+ "bytes": 3404342,
+ "sha256": "683c13ef47fe913904b73282ecc98e5a2577e3a4a616d4ac1f4f737e5b3e8c31",
+ "key": "runtime-assets/683c13ef47fe913904b73282ecc98e5a2577e3a4a616d4ac1f4f737e5b3e8c31/layers/z-detail.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-00.webp",
+ "bytes": 122,
+ "sha256": "35d5dc3ad1a75292bb9da6897e37d85c72a04c7c7c929dd157999a1c3f62731a",
+ "key": "runtime-assets/35d5dc3ad1a75292bb9da6897e37d85c72a04c7c7c929dd157999a1c3f62731a/layers/x-00.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-01.webp",
+ "bytes": 1722,
+ "sha256": "72c8864fee520d58bc55486580be019ad22235f9613d4b650aa714ce8455d5c9",
+ "key": "runtime-assets/72c8864fee520d58bc55486580be019ad22235f9613d4b650aa714ce8455d5c9/layers/x-01.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-02.webp",
+ "bytes": 2288,
+ "sha256": "8116cd8f718623d7a5c64780852f43743d77e6cb502015188015734b62a77120",
+ "key": "runtime-assets/8116cd8f718623d7a5c64780852f43743d77e6cb502015188015734b62a77120/layers/x-02.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-03.webp",
+ "bytes": 2254,
+ "sha256": "7a039f59b82b66140d7846e6c42af9610e5d1528d0a5753764038d076c6070ef",
+ "key": "runtime-assets/7a039f59b82b66140d7846e6c42af9610e5d1528d0a5753764038d076c6070ef/layers/x-03.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-04.webp",
+ "bytes": 2494,
+ "sha256": "140005f9292e15140049210e50907fdac8f25c1d23ae5cf53420ba0076312931",
+ "key": "runtime-assets/140005f9292e15140049210e50907fdac8f25c1d23ae5cf53420ba0076312931/layers/x-04.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-05.webp",
+ "bytes": 2638,
+ "sha256": "56868d53d8504d62f6d128d97ed8e64f28bf47014b6e61d430d241ba37ca0766",
+ "key": "runtime-assets/56868d53d8504d62f6d128d97ed8e64f28bf47014b6e61d430d241ba37ca0766/layers/x-05.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-06.webp",
+ "bytes": 2756,
+ "sha256": "9da3c0bf6600982ba8311694c4a3720f050f172d244e917302b07ec70226e146",
+ "key": "runtime-assets/9da3c0bf6600982ba8311694c4a3720f050f172d244e917302b07ec70226e146/layers/x-06.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-07.webp",
+ "bytes": 2722,
+ "sha256": "f20ac9fd2dd466910602c93e5e929c30735717bf8b292ee05e818374a19120d7",
+ "key": "runtime-assets/f20ac9fd2dd466910602c93e5e929c30735717bf8b292ee05e818374a19120d7/layers/x-07.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-08.webp",
+ "bytes": 2922,
+ "sha256": "3b081a11d1da314987c4c68de6fee632676d88751610f77bafb6b4a66130ce40",
+ "key": "runtime-assets/3b081a11d1da314987c4c68de6fee632676d88751610f77bafb6b4a66130ce40/layers/x-08.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-09.webp",
+ "bytes": 2894,
+ "sha256": "b67d875f5733c94041882ff384aa090a2aaf6ace1a6bb739d5732fe81862fa86",
+ "key": "runtime-assets/b67d875f5733c94041882ff384aa090a2aaf6ace1a6bb739d5732fe81862fa86/layers/x-09.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-10.webp",
+ "bytes": 2932,
+ "sha256": "a4aa9ab2e15cd8ff874bcecbcaa1ada6503981efbe3808d9aeefbd0a1bdc4f10",
+ "key": "runtime-assets/a4aa9ab2e15cd8ff874bcecbcaa1ada6503981efbe3808d9aeefbd0a1bdc4f10/layers/x-10.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-11.webp",
+ "bytes": 2880,
+ "sha256": "a57aa43aa5f8edf7f45ace919e65bfb31c8ecffe49d7e09458f6f8ea271a4ac2",
+ "key": "runtime-assets/a57aa43aa5f8edf7f45ace919e65bfb31c8ecffe49d7e09458f6f8ea271a4ac2/layers/x-11.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-12.webp",
+ "bytes": 3924,
+ "sha256": "f77aab646f679802c1d539bf9698cbb70a9f48e36358a0e8ca7fb0f081ff2b0f",
+ "key": "runtime-assets/f77aab646f679802c1d539bf9698cbb70a9f48e36358a0e8ca7fb0f081ff2b0f/layers/x-12.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-13.webp",
+ "bytes": 3528,
+ "sha256": "faf9f6f168641a7afea3de68bdc6d0083f7a3f82941aaaa2900e6b194ce598e5",
+ "key": "runtime-assets/faf9f6f168641a7afea3de68bdc6d0083f7a3f82941aaaa2900e6b194ce598e5/layers/x-13.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-14.webp",
+ "bytes": 3800,
+ "sha256": "5d69273f953fa930eebed5d89e71bf2194ee4557ce8cce50a0f0aff166b282cf",
+ "key": "runtime-assets/5d69273f953fa930eebed5d89e71bf2194ee4557ce8cce50a0f0aff166b282cf/layers/x-14.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-15.webp",
+ "bytes": 4056,
+ "sha256": "e39d0c714ffbd3e62c237a5abeee06fd33cacaa2563370f95cd6cc8302e82e43",
+ "key": "runtime-assets/e39d0c714ffbd3e62c237a5abeee06fd33cacaa2563370f95cd6cc8302e82e43/layers/x-15.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-16.webp",
+ "bytes": 4036,
+ "sha256": "d92a95e7a7f0feacdb100ca9b921f0ae88e63826e499df33c361291aa493a986",
+ "key": "runtime-assets/d92a95e7a7f0feacdb100ca9b921f0ae88e63826e499df33c361291aa493a986/layers/x-16.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-17.webp",
+ "bytes": 3730,
+ "sha256": "0ec312513b4cbae6c094f930f2dfe5fc42237ea88a37b2f34dd16e0ea37a0a54",
+ "key": "runtime-assets/0ec312513b4cbae6c094f930f2dfe5fc42237ea88a37b2f34dd16e0ea37a0a54/layers/x-17.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-18.webp",
+ "bytes": 4060,
+ "sha256": "2fd1ecf0fe47d904eda3b46f8249771544b4cf60c25b851352be383b6adc486d",
+ "key": "runtime-assets/2fd1ecf0fe47d904eda3b46f8249771544b4cf60c25b851352be383b6adc486d/layers/x-18.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-19.webp",
+ "bytes": 3932,
+ "sha256": "531f166a3156312adfd5ab1ce56b1853e405af05f0f9d80a75d9a9939177276b",
+ "key": "runtime-assets/531f166a3156312adfd5ab1ce56b1853e405af05f0f9d80a75d9a9939177276b/layers/x-19.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-20.webp",
+ "bytes": 3202,
+ "sha256": "901a31717542c9214518cff2a3901ec8e7bb1e0a423df020704691738eecc92c",
+ "key": "runtime-assets/901a31717542c9214518cff2a3901ec8e7bb1e0a423df020704691738eecc92c/layers/x-20.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-21.webp",
+ "bytes": 2944,
+ "sha256": "b694f6808551b3ed520c6979bb19b3d6ec36358c16011e13153ba09721d5ee1a",
+ "key": "runtime-assets/b694f6808551b3ed520c6979bb19b3d6ec36358c16011e13153ba09721d5ee1a/layers/x-21.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-22.webp",
+ "bytes": 3066,
+ "sha256": "ef8a1f0cc3267af10d6c294b6c562da9738fa055a0f8d726505644715112d110",
+ "key": "runtime-assets/ef8a1f0cc3267af10d6c294b6c562da9738fa055a0f8d726505644715112d110/layers/x-22.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-23.webp",
+ "bytes": 3060,
+ "sha256": "9e81aabfc770c9d936458e061c6c88bb6a12a0de28ffd7752432b0b87aa7b8a5",
+ "key": "runtime-assets/9e81aabfc770c9d936458e061c6c88bb6a12a0de28ffd7752432b0b87aa7b8a5/layers/x-23.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-24.webp",
+ "bytes": 3190,
+ "sha256": "6075a88ffaa26776ba0ffa8f1bd7247e4902548a2684ad85dacd18c6590d893c",
+ "key": "runtime-assets/6075a88ffaa26776ba0ffa8f1bd7247e4902548a2684ad85dacd18c6590d893c/layers/x-24.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-25.webp",
+ "bytes": 2910,
+ "sha256": "d1421dc8b2d2d166c6fbda6dab3c6dd88be88df48e61a792875a6b024490d73f",
+ "key": "runtime-assets/d1421dc8b2d2d166c6fbda6dab3c6dd88be88df48e61a792875a6b024490d73f/layers/x-25.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-26.webp",
+ "bytes": 2760,
+ "sha256": "4e4807c3a1fe8ca1978344545582b83ee2be935414f84a494651bd325d160e44",
+ "key": "runtime-assets/4e4807c3a1fe8ca1978344545582b83ee2be935414f84a494651bd325d160e44/layers/x-26.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-27.webp",
+ "bytes": 2544,
+ "sha256": "13f3e17314addea720fbf3a510ad949aa1649f3e8e08f7d642ca08cee3d3f7d2",
+ "key": "runtime-assets/13f3e17314addea720fbf3a510ad949aa1649f3e8e08f7d642ca08cee3d3f7d2/layers/x-27.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-28.webp",
+ "bytes": 2354,
+ "sha256": "dc91558ab3c06726f14ae9e27bef95954bbc3d3eef826338dc9ba9f0baf2d882",
+ "key": "runtime-assets/dc91558ab3c06726f14ae9e27bef95954bbc3d3eef826338dc9ba9f0baf2d882/layers/x-28.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-29.webp",
+ "bytes": 2250,
+ "sha256": "5e0806b77e73aeb2b906728a8038eabedfcddff7673898d194b91e9701fb9534",
+ "key": "runtime-assets/5e0806b77e73aeb2b906728a8038eabedfcddff7673898d194b91e9701fb9534/layers/x-29.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-30.webp",
+ "bytes": 1344,
+ "sha256": "8c45c2ed0f0c3d8ca0578109f13cbcb07a00d237cceae0c75ad7cbe2fca6bb38",
+ "key": "runtime-assets/8c45c2ed0f0c3d8ca0578109f13cbcb07a00d237cceae0c75ad7cbe2fca6bb38/layers/x-30.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/x-31.webp",
+ "bytes": 678,
+ "sha256": "73fcf60f7dd711a814bf9a706e74af30ee09de772a37aab9a3282bb36033b488",
+ "key": "runtime-assets/73fcf60f7dd711a814bf9a706e74af30ee09de772a37aab9a3282bb36033b488/layers/x-31.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-00.webp",
+ "bytes": 470,
+ "sha256": "36ada998ef860f0bc340bc7f44a37d873894738b66be115eaf47ccbd651a404b",
+ "key": "runtime-assets/36ada998ef860f0bc340bc7f44a37d873894738b66be115eaf47ccbd651a404b/layers/y-00.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-01.webp",
+ "bytes": 1532,
+ "sha256": "1415d833d90edc69c8d42f8ab40366f6f0079843018dc0c1ae169625b71f8fa9",
+ "key": "runtime-assets/1415d833d90edc69c8d42f8ab40366f6f0079843018dc0c1ae169625b71f8fa9/layers/y-01.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-02.webp",
+ "bytes": 2132,
+ "sha256": "e638e87f8e27fe0995b2b3819e53c823a2be3cbf3d63ec71a847765e8dbad83a",
+ "key": "runtime-assets/e638e87f8e27fe0995b2b3819e53c823a2be3cbf3d63ec71a847765e8dbad83a/layers/y-02.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-03.webp",
+ "bytes": 2412,
+ "sha256": "0582883b575abeec945ec89597123dc9bd8399f3162ace31c3ab2d9d520e0aa2",
+ "key": "runtime-assets/0582883b575abeec945ec89597123dc9bd8399f3162ace31c3ab2d9d520e0aa2/layers/y-03.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-04.webp",
+ "bytes": 2428,
+ "sha256": "2dc0ebec1569270450ed7ff8ab3bd39431e7e2677323fb3cc8f8267a4a956a4a",
+ "key": "runtime-assets/2dc0ebec1569270450ed7ff8ab3bd39431e7e2677323fb3cc8f8267a4a956a4a/layers/y-04.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-05.webp",
+ "bytes": 2624,
+ "sha256": "53b834cd8c5edcc015792ba6bb424514c566bb3bbb07818d53fed58f848e0182",
+ "key": "runtime-assets/53b834cd8c5edcc015792ba6bb424514c566bb3bbb07818d53fed58f848e0182/layers/y-05.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-06.webp",
+ "bytes": 3006,
+ "sha256": "17120a5e08a5f8ec1b2a7dbd8d7b02b6adc83cdb73ed725bcd1ac2da251782bc",
+ "key": "runtime-assets/17120a5e08a5f8ec1b2a7dbd8d7b02b6adc83cdb73ed725bcd1ac2da251782bc/layers/y-06.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-07.webp",
+ "bytes": 3122,
+ "sha256": "6f3232d965a361b3e30daf51f650c1a80cb2252b2f552d10ae2f8db39b680d04",
+ "key": "runtime-assets/6f3232d965a361b3e30daf51f650c1a80cb2252b2f552d10ae2f8db39b680d04/layers/y-07.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-08.webp",
+ "bytes": 3174,
+ "sha256": "3791a24601502544ba4822d5ca6580461db82b78ca22d293f1d96a97141ef949",
+ "key": "runtime-assets/3791a24601502544ba4822d5ca6580461db82b78ca22d293f1d96a97141ef949/layers/y-08.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-09.webp",
+ "bytes": 3214,
+ "sha256": "b6f854ab7c835c2c55e7e9e2ab6471524e7dff527e30b139956a31819035920e",
+ "key": "runtime-assets/b6f854ab7c835c2c55e7e9e2ab6471524e7dff527e30b139956a31819035920e/layers/y-09.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-10.webp",
+ "bytes": 3324,
+ "sha256": "9b6f007f5a8bd88c4ac2985bc43c42423b0ba57e8209922fe0a6923f5d6f118e",
+ "key": "runtime-assets/9b6f007f5a8bd88c4ac2985bc43c42423b0ba57e8209922fe0a6923f5d6f118e/layers/y-10.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-11.webp",
+ "bytes": 3626,
+ "sha256": "0ec70fbed41fd7ec7075b0e812a92d8407a84404ae661e2e4e135765ee886fb5",
+ "key": "runtime-assets/0ec70fbed41fd7ec7075b0e812a92d8407a84404ae661e2e4e135765ee886fb5/layers/y-11.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-12.webp",
+ "bytes": 3152,
+ "sha256": "0254ea3f4a2f37882c286ae33643fdb6bc71c0edde6c09862e91a3b6620c0db1",
+ "key": "runtime-assets/0254ea3f4a2f37882c286ae33643fdb6bc71c0edde6c09862e91a3b6620c0db1/layers/y-12.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-13.webp",
+ "bytes": 4162,
+ "sha256": "57d0614e8729b6aa65f26866bbf663ab8cd4e6f92864e93703747575e21427e8",
+ "key": "runtime-assets/57d0614e8729b6aa65f26866bbf663ab8cd4e6f92864e93703747575e21427e8/layers/y-13.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-14.webp",
+ "bytes": 4362,
+ "sha256": "9b1f4c8bbdb8b3470e3fd10064d79817895243b35a1fb602fd9e48dc76ef52da",
+ "key": "runtime-assets/9b1f4c8bbdb8b3470e3fd10064d79817895243b35a1fb602fd9e48dc76ef52da/layers/y-14.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-15.webp",
+ "bytes": 4368,
+ "sha256": "4573521f262444a362732200e30d7fd237fa62265abef54198525e97bbcb7794",
+ "key": "runtime-assets/4573521f262444a362732200e30d7fd237fa62265abef54198525e97bbcb7794/layers/y-15.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-16.webp",
+ "bytes": 4212,
+ "sha256": "4f55b1204884a4c7781ab26db1c78f60bbb5e8c8ac545fbad2cd7893fbce780e",
+ "key": "runtime-assets/4f55b1204884a4c7781ab26db1c78f60bbb5e8c8ac545fbad2cd7893fbce780e/layers/y-16.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-17.webp",
+ "bytes": 4270,
+ "sha256": "52b59cfd081f33e8d466669d3163f18b1dd90b16b00157e2e4a0908228e2a084",
+ "key": "runtime-assets/52b59cfd081f33e8d466669d3163f18b1dd90b16b00157e2e4a0908228e2a084/layers/y-17.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-18.webp",
+ "bytes": 4118,
+ "sha256": "c2e078d4be49d638fd955d4b736cade4e5ddd2e86b1dc945a9e81d668860868e",
+ "key": "runtime-assets/c2e078d4be49d638fd955d4b736cade4e5ddd2e86b1dc945a9e81d668860868e/layers/y-18.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-19.webp",
+ "bytes": 3336,
+ "sha256": "5843cf3cf18325a814cc278f8ea900c96fbeffbadab8d2fd0331a1d1029b4051",
+ "key": "runtime-assets/5843cf3cf18325a814cc278f8ea900c96fbeffbadab8d2fd0331a1d1029b4051/layers/y-19.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-20.webp",
+ "bytes": 4102,
+ "sha256": "068c6b6c59a9711478b32830a5c0b4b57125051dbce126320c512c6c7a6fed91",
+ "key": "runtime-assets/068c6b6c59a9711478b32830a5c0b4b57125051dbce126320c512c6c7a6fed91/layers/y-20.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-21.webp",
+ "bytes": 3492,
+ "sha256": "9eb898dd4b7270594b4e518f97522b4170479c38dcb82dcab90bae4975d3ca74",
+ "key": "runtime-assets/9eb898dd4b7270594b4e518f97522b4170479c38dcb82dcab90bae4975d3ca74/layers/y-21.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-22.webp",
+ "bytes": 2920,
+ "sha256": "9961c67624b32d763a9f1df1cfdcd756daee1b2242d00bc1248e594cb6354f1b",
+ "key": "runtime-assets/9961c67624b32d763a9f1df1cfdcd756daee1b2242d00bc1248e594cb6354f1b/layers/y-22.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-23.webp",
+ "bytes": 2512,
+ "sha256": "2853242227d6a97b992006bf773fdac3bb74ecac08a7ef5f3bff28d151eae590",
+ "key": "runtime-assets/2853242227d6a97b992006bf773fdac3bb74ecac08a7ef5f3bff28d151eae590/layers/y-23.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-24.webp",
+ "bytes": 2634,
+ "sha256": "743e0747fadff33bc77c4070d3a9b04fe15ecb46d500c96c5cdab9f47a89b6db",
+ "key": "runtime-assets/743e0747fadff33bc77c4070d3a9b04fe15ecb46d500c96c5cdab9f47a89b6db/layers/y-24.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-25.webp",
+ "bytes": 2186,
+ "sha256": "373df984de9b7ad0d6d95809a3299dfce704f2269ebb5435ac1665610218301d",
+ "key": "runtime-assets/373df984de9b7ad0d6d95809a3299dfce704f2269ebb5435ac1665610218301d/layers/y-25.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-26.webp",
+ "bytes": 2352,
+ "sha256": "5508cb54423a2d5cafc244aefc83d86ace0c03472b92066b39a1443c7d7ec154",
+ "key": "runtime-assets/5508cb54423a2d5cafc244aefc83d86ace0c03472b92066b39a1443c7d7ec154/layers/y-26.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-27.webp",
+ "bytes": 2372,
+ "sha256": "f4e7a820e0c094c7de27b35bfa9d19623f394984c5e0185a6cc19331f909fd99",
+ "key": "runtime-assets/f4e7a820e0c094c7de27b35bfa9d19623f394984c5e0185a6cc19331f909fd99/layers/y-27.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-28.webp",
+ "bytes": 2012,
+ "sha256": "13797069fdc06f06d82fa454f5da8fcdc02bf32deff81c72b7d678c0e4804262",
+ "key": "runtime-assets/13797069fdc06f06d82fa454f5da8fcdc02bf32deff81c72b7d678c0e4804262/layers/y-28.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-29.webp",
+ "bytes": 2010,
+ "sha256": "d82a866103e6874d481c56db887e626ff727024437f335d3fdeddcf08934c78a",
+ "key": "runtime-assets/d82a866103e6874d481c56db887e626ff727024437f335d3fdeddcf08934c78a/layers/y-29.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "layers/y-30.webp",
+ "bytes": 1556,
+ "sha256": "e3ed8ddb23297696a321333d21e78c8733569e1e143c79a0cfb37130e42aebd3",
+ "key": "runtime-assets/e3ed8ddb23297696a321333d21e78c8733569e1e143c79a0cfb37130e42aebd3/layers/y-30.webp"
+ },
+ {
+ "id": "m33",
+ "filename": "provenance.json",
+ "bytes": 37975,
+ "sha256": "2db0a9aa6db465c0eb759ffa694dc4c68f8df98f8e68ac5fb7913e8f0426785d",
+ "key": "runtime-assets/2db0a9aa6db465c0eb759ffa694dc4c68f8df98f8e68ac5fb7913e8f0426785d/provenance.json"
+ },
+ {
+ "id": "m33",
+ "filename": "presentation.json",
+ "bytes": 1799,
+ "sha256": "9193e89bf84c577cf77f8fb4d584505b03a069264f009959ece9df73c6c59d71",
+ "key": "runtime-assets/9193e89bf84c577cf77f8fb4d584505b03a069264f009959ece9df73c6c59d71/presentation.json"
+ },
+ {
+ "id": "m33",
+ "filename": "datasets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3.webp",
+ "bytes": 59744,
+ "sha256": "d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3",
+ "key": "runtime-assets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3/datasets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3.webp"
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "points.json",
+ "bytes": 345406,
+ "sha256": "595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee",
+ "key": "runtime-assets/595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee/points.json"
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "cloud.webp",
+ "bytes": 1706,
+ "sha256": "ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c",
+ "key": "runtime-assets/ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c/cloud.webp"
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "provenance.json",
+ "bytes": 10354,
+ "sha256": "d212db52a17a691903764ee360ea3cd30427b7290771f7a4581b00de1ce61b5d",
+ "key": "runtime-assets/d212db52a17a691903764ee360ea3cd30427b7290771f7a4581b00de1ce61b5d/provenance.json"
+ },
+ {
+ "id": "nearby-universe",
+ "filename": "presentation.json",
+ "bytes": 490,
+ "sha256": "224e620caa689a95820bb15ad87bc37e7c7a6c2489db2136a441096246b10889",
+ "key": "runtime-assets/224e620caa689a95820bb15ad87bc37e7c7a6c2489db2136a441096246b10889/presentation.json"
+ }
+ ],
+ "requests": [
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01/layers/z-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05/layers/z-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35/layers/z-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596/layers/z-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4/layers/z-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137/layers/z-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353/layers/z-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35/layers/z-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4/layers/z-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f/layers/z-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572/layers/z-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3/layers/z-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911/layers/z-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93/layers/z-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284/layers/z-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b9d4c47cd039ce3d1498f85934c0a4a2751a30c425c8509f1da44496d8e7a284/layers/z-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433/layers/z-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c09db53155371c9a4cff83fdf6b0ad626fd66228ce7852704be71e0f65204aa3/layers/z-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d0ac1555a2d798ec9f743d8a0a7495205d76d77f1bf34c3883db7cdb58c4d433/layers/z-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b3f98769e80cfed1e7b3a74ee762cc759a2e9aae69644344fffc7878d3133572/layers/z-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cf44d8d26010d5b1bb1c3169627e3949a46abcaffe4f07ea6d3fd2dd1755e911/layers/z-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3f49381367de448f2bcd386189df20045fb39e296cd5f9aa1e87783172908e2f/layers/z-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9333de325161d8a3f7c3389184b71511a3cc48f0c3476087704e93d1b7b43e93/layers/z-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/118eef959737740acd77b7b8f9c5df313211cdf59e98d4a0ac29e4c6807f0d35/layers/z-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/77f416074bf14e0b83e21d64ead1f7fbd9dd73d5cd90140b8c60843df97e13a4/layers/z-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/421e3c3200a0980fa31315a49e744c1def6514d1baf655e3c515e54ded060f35/layers/z-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/567d2909414dcb13e93c9c108de91adb19457a04330b5d7e982ce4a210867137/layers/z-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7003f40f53b49937b7b44b00945ee56b7a3adf6a96ee0d2a8df029339a4c5ca4/layers/z-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b29d75622c21eeb6e5bc0e10140ef81735a28bae64c84eac9b7f5385a25da05/layers/z-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/db6ec728c24a01de00d7bb5936158a5ad306340c412ab96ae5c7beeab6cb4596/layers/z-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6bb606e1b128884228a418c2d87db98dca783abbc8b0c0e9e9bfa3678f516353/layers/z-31.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7da54ef71c1872bf2da0180a62f5d516a8f524b56da9405c3bf8c5a1c6b0009c/layers/z-detail.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/16951a0b2f8fcccdd5c9b7c419cd6e486fda759448437ffe06fc6ba20a3b3957/image-layers.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e78eeeb4e4bff12c71779eb59ad753e333a202b46a8e6482932b8379509f0f5c/layers/x-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4183e67bfc559a17b5b747933152d6787bf654f6bf6663bd15f18ea96e82fc01/layers/z-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d464be75e453dc4a95e26c7fb38c5f46e6a7a0eb37497dad32c3cfe0db95938f/layers/x-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a973db4172b7d63477c0ca311ddd238f7f13fa3bbc6a1bdb8f4b1e0b9b11896a/layers/x-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ab3c45ef714a5e9e051f58cd1089d4c5d0d83c93d0ef5db193caab6e1a445d6e/layers/x-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/01f47abf55059be7fd2647523fc87600e2d63834f8c7e70640b5711c15638c9b/layers/x-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d46f27c04493425a415a5d39e2a9e9acc2eb2f9b04682e3db83fad6dfe638e49/layers/x-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2de4319c85aaab323cedaeedfc86bdab53db0bfc33019e6eeb3d8ccca2ae97f1/layers/x-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e71c5a2506fdceedb650dd2babc5e73cc69fe4beea48278782749fd1840e5a6d/layers/x-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6da86dca8873f6d5bad23a049050ad14b0bb186484dba9d4e5f8601f95d9dcf6/layers/x-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/eb854b16b3824e8e47f87f879e56808868f689e036cd4483dd01bd78457f83a8/layers/x-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/066d7e6d9b2057384cca5e78af641df790f5ccabdee90fe68b0ac793e3714267/layers/x-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f094f5781ece11c3306c3343bcf0267b3a77ba83f69aa4045ac222bcca5863c5/layers/x-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e55df8e13829273a44e7c03d2d210d3ee8ac8a444f3e4b90a52d497427260412/layers/x-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/add47432f770ba703f598efb70a50dac12af9d03ead2de75d3c1e9c878f91d30/layers/x-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/900b1aada73e64f398e8a0245f28ae7bf651740879f64c71f18ac604293e407d/layers/x-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3fae9a41364c44e2687c51fd3fd215a5af3ab59f46dce8b0a7fa1d8e80f1e76f/layers/x-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/262b930cf8a41787c65a3b2f4089106f5a9d379e122f56067d4239a84d65e61e/layers/x-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/886d478112b4f95449d46d239d253649c8c54ac652349ca8ddbed7436ac80a61/layers/x-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/15390b6c4c5c8cfd19c85857df4b9aaf80f0dd701c62ef665ad75c445cfd00ea/layers/x-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d4741e12d20490cc650b95df8c8b7e1ff2615f148be0a759592bd7684b566729/layers/x-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/14fd79cc7ad12c1d9f7f9e730c0a5d928e8b4eede72216c4074a15b778ef45df/layers/x-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8806454f09e05a8242169fb729ffd62353ad6dec22e6372e20936ed6cd9afcb8/layers/x-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7229f90e841349a0e3040a7ba36cb8e6a6fd031786ba276d652e9966cca9b352/layers/x-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5275337df162438547ce51109b8825bc30c08a4b58e4e982945f343643d91058/layers/x-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/012fa982feec9ee649988eefe9d359394c6266c67397c086afbc7bfecde4a3ea/layers/x-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/aaf60d06e5b0a7b9136f9aac1a518c1bf59c72b2d7734f5f4b74305a3c3bc3ea/layers/x-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1cb5d9e6b724c19e5800d8fe1a093cce2ed89146dff460952b703374491e43ee/layers/x-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7be4374bf82ad267a3412e7c318c13e86c03b6160fb7d3ad1ef1f10800af2bde/layers/x-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c0ecd45f8bce89e8bcb5b6b2b419e8d4bb2ce63967154a7a3cfe9a854e8a30c8/layers/y-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d369ea41b48f54cde8701fa2ca046a1500658967a22e472af9671d5f07a611a9/layers/y-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ef21f88b9a5a3fe6caf0f5eb77dec87fa0e9fa776ef208ab3177f75b83d779ef/layers/y-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/872d95de54845c3768c0e638de78f8f43c6fc1f9bc233988bea64d53ca86d821/layers/y-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1877add88dec4803e86ad04981d6a616125cbb4a9b23828e10661ac0df3a29a4/layers/y-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ad8c4648010f0facc726cf6f64de2421045c05b50756876960bf550f6168e02e/layers/y-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b3129189d8e02ba191f73fdd6ef0f74a6fad646cabbc7df354205e8438b90b57/layers/y-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/670683e45cc76f09d2fabaa8c33d0de4f985e6a3e408dafbc9f42853c450ebae/layers/y-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c288ad201a5644b8521a5d9a20a67479b89d2bcd24354ab17b9f4ef67d845db3/layers/y-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ec870ddaeaa7615f5d8ab0f5d46a78d320aaf9fc59b806e2ffa1df85a91057ec/layers/y-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/dfafcb4e68bac5394285b3d7ea589ff43444f3732d76db17d363e59fa694d454/layers/y-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/334b0a6ce53fcdeff82133aeabba8286dbe71e1463915b9c8af55663b60170dc/layers/y-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/455eb90107ff596122362fb42d9f979f55a65734e9290eefd3a2888155999ce4/layers/y-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3f368bbbe73ca3a4a9fdcf6aecd1413aee471aaf8eba6da51df7e4c7d997fe1f/layers/y-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cfb523ee09f4f5a755910c5b86f3b8227ef46f6035eb2d5493eedcc84583c098/layers/y-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c895666cb91769fca707167be46d0ba6f3285e295aa0cf06afcb734bf775b3be/layers/y-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1d9665c4314ff93e641de2368f9ea887d2fe6f81c570a7f3a3606d4a952084e4/layers/y-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c3d435478a2f011e0f09d0a99293cfcdb88b57ffbb1613635999478bf4b50bbf/layers/y-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0ca061c1e4cb504647e15696f74fde84b5f3a9685f62562a0342536c2ca724b4/layers/y-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/69e82473c09d985ee2b9d163c9ff249f0bca837b0b47834575435e2b05edcf6f/layers/y-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/040001269ace310d7afc32e4dffb240878009d3d033b64b399c0c49c720bb2f6/layers/y-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/262b3347921ae8a48a60a2395616aeeeca477312bdc8e4343d9531b392b8c711/layers/y-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7f427707bf01b3682239f90b165a3811617fd97dfab59b4f18903402a643b615/layers/y-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1ab16b4007a25fcc92b58788f7be38c3e928e5263bf940f5d3e183cab2d88a57/layers/y-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ced11e3d3bd1f5ef96646ce2f7b7f7e8f54047e57398fda77c8d24146659b429/layers/y-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e198fb0a565b3402bf3789a166d618cd645f736600158378d2ca73e112300976/layers/y-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0764e92999e6a776fd15124558847e763a0bf585e3fe33fa36fe90ddaf7c27ed/layers/y-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/bdb46824a992a8cd31c9c91cd6bc0987d390359762f70eb12c194d17288985f2/layers/y-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ad902a30624a2659ab0e680739684698a3e1348305e6ee0920f879dd9a04fdb7/layers/y-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/60da9e0b67cf85fbec7fbab7530827fdc874a0443e225acfd9d21ba7e98357ab/layers/y-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/61303a62465dd706e6f1ddb23dba2c16c89d0493e67c16b67f38e890242cb4f3/layers/y-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9/datasets/4c152fc4d7ea84f7b4b0676fbd81dd3d0400838d2ece4f60f44d93c98ab70de9.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe/layers/z-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd/layers/z-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f/layers/z-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8/layers/z-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb/layers/z-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c/layers/z-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4/layers/z-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d/layers/z-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67/layers/z-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b/layers/z-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e/layers/z-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee/layers/z-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b/layers/z-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ef01151d319c4a1ad480d65a58cafaf24db1b3d28ac5deab5bcb0a420d36dbe1/presentation.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac/layers/z-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3/layers/z-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d96140b77831ccd093d0da69a5cf4659ddc53d4e11d2c686d8216854a13cdef1/provenance.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8/layers/z-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c2a1e602eb35ab3fa2627d47ecdc3975cba4c59a41f3fffa0c819e79a0f33af3/layers/z-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1e89f7d7ea9673eeebbbc3ad6da2695202dce3dfd2281830aa5fa5ff1c9e6572/image-layers.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4072d77692a612b5b8ece100b61d5c40393182d17d83139e93f07be80fdc23a8/layers/z-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7b710550765b48d1d955afb80d177756d8ad30a48947b3b65b497f1bbf1d7aac/layers/z-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cd129ce019967ea60f4a765e1b23da720dfd9db4f1c58d615f4d385006d31d9b/layers/z-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6398b3c2bc5294ca4698872c9c9ecb352828b28c31b133ba0344ae5acc380a3b/layers/z-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/69b82a8bdb4912939fceb1e62bb04d68a4be7b7f5fbd68ceea063fbbdf46f2ee/layers/z-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ed344dfdf8533c4adaf61fb1493f2bdb807605c9ad1f9ec6e474982883f81b1e/layers/z-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4406101dfecdcad87e52949cb9782fea0a1a346b79d4ef8667a8c982408ddd67/layers/z-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d6f82bea1fca7aa8c88cc637f1a59361bd8349abc3f71dbfc7ececf4ae9a16b4/layers/z-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/476b196d338f9acc31df0d86e203ba2d84a8acc605d2454ab179c30a1201996d/layers/z-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/90c739ac343ec57c552b11c078c2ca9d7edc7fa591d02bb9b7e1cf0a4204e13c/layers/z-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e48d5cc75f7915033ce7518318b07cd6bf1562d8e3f9f31ae7fc11778a9674a8/layers/z-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4c5e563ff8d48894c95299cd1950e6da4c0e506fe53116d42b297393fb120abb/layers/z-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cf485e4b6ead9ea6d1cfdfe005c5f4d3415c0b55f1f2f620de748fb12652ef6f/layers/z-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5ecaca08e277ab2ff60b25a11847f4efca5be617c5a1d15715e946703e26a3dd/layers/z-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/683c13ef47fe913904b73282ecc98e5a2577e3a4a616d4ac1f4f737e5b3e8c31/layers/z-detail.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d0ccf53fe78a94c41a6d0cd07f1356d1da7e553bacada03fcae3653dcab30abe/layers/z-31.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/35d5dc3ad1a75292bb9da6897e37d85c72a04c7c7c929dd157999a1c3f62731a/layers/x-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/72c8864fee520d58bc55486580be019ad22235f9613d4b650aa714ce8455d5c9/layers/x-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8116cd8f718623d7a5c64780852f43743d77e6cb502015188015734b62a77120/layers/x-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9da3c0bf6600982ba8311694c4a3720f050f172d244e917302b07ec70226e146/layers/x-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/56868d53d8504d62f6d128d97ed8e64f28bf47014b6e61d430d241ba37ca0766/layers/x-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/140005f9292e15140049210e50907fdac8f25c1d23ae5cf53420ba0076312931/layers/x-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7a039f59b82b66140d7846e6c42af9610e5d1528d0a5753764038d076c6070ef/layers/x-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f20ac9fd2dd466910602c93e5e929c30735717bf8b292ee05e818374a19120d7/layers/x-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3b081a11d1da314987c4c68de6fee632676d88751610f77bafb6b4a66130ce40/layers/x-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b67d875f5733c94041882ff384aa090a2aaf6ace1a6bb739d5732fe81862fa86/layers/x-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a57aa43aa5f8edf7f45ace919e65bfb31c8ecffe49d7e09458f6f8ea271a4ac2/layers/x-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/a4aa9ab2e15cd8ff874bcecbcaa1ada6503981efbe3808d9aeefbd0a1bdc4f10/layers/x-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f77aab646f679802c1d539bf9698cbb70a9f48e36358a0e8ca7fb0f081ff2b0f/layers/x-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/faf9f6f168641a7afea3de68bdc6d0083f7a3f82941aaaa2900e6b194ce598e5/layers/x-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5d69273f953fa930eebed5d89e71bf2194ee4557ce8cce50a0f0aff166b282cf/layers/x-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e39d0c714ffbd3e62c237a5abeee06fd33cacaa2563370f95cd6cc8302e82e43/layers/x-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d92a95e7a7f0feacdb100ca9b921f0ae88e63826e499df33c361291aa493a986/layers/x-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0ec312513b4cbae6c094f930f2dfe5fc42237ea88a37b2f34dd16e0ea37a0a54/layers/x-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/531f166a3156312adfd5ab1ce56b1853e405af05f0f9d80a75d9a9939177276b/layers/x-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b694f6808551b3ed520c6979bb19b3d6ec36358c16011e13153ba09721d5ee1a/layers/x-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2fd1ecf0fe47d904eda3b46f8249771544b4cf60c25b851352be383b6adc486d/layers/x-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ef8a1f0cc3267af10d6c294b6c562da9738fa055a0f8d726505644715112d110/layers/x-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/901a31717542c9214518cff2a3901ec8e7bb1e0a423df020704691738eecc92c/layers/x-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9e81aabfc770c9d936458e061c6c88bb6a12a0de28ffd7752432b0b87aa7b8a5/layers/x-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6075a88ffaa26776ba0ffa8f1bd7247e4902548a2684ad85dacd18c6590d893c/layers/x-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d1421dc8b2d2d166c6fbda6dab3c6dd88be88df48e61a792875a6b024490d73f/layers/x-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4e4807c3a1fe8ca1978344545582b83ee2be935414f84a494651bd325d160e44/layers/x-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/13f3e17314addea720fbf3a510ad949aa1649f3e8e08f7d642ca08cee3d3f7d2/layers/x-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8c45c2ed0f0c3d8ca0578109f13cbcb07a00d237cceae0c75ad7cbe2fca6bb38/layers/x-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/73fcf60f7dd711a814bf9a706e74af30ee09de772a37aab9a3282bb36033b488/layers/x-31.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5e0806b77e73aeb2b906728a8038eabedfcddff7673898d194b91e9701fb9534/layers/x-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/dc91558ab3c06726f14ae9e27bef95954bbc3d3eef826338dc9ba9f0baf2d882/layers/x-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1415d833d90edc69c8d42f8ab40366f6f0079843018dc0c1ae169625b71f8fa9/layers/y-01.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/36ada998ef860f0bc340bc7f44a37d873894738b66be115eaf47ccbd651a404b/layers/y-00.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e638e87f8e27fe0995b2b3819e53c823a2be3cbf3d63ec71a847765e8dbad83a/layers/y-02.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0582883b575abeec945ec89597123dc9bd8399f3162ace31c3ab2d9d520e0aa2/layers/y-03.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/17120a5e08a5f8ec1b2a7dbd8d7b02b6adc83cdb73ed725bcd1ac2da251782bc/layers/y-06.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3791a24601502544ba4822d5ca6580461db82b78ca22d293f1d96a97141ef949/layers/y-08.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9b6f007f5a8bd88c4ac2985bc43c42423b0ba57e8209922fe0a6923f5d6f118e/layers/y-10.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/53b834cd8c5edcc015792ba6bb424514c566bb3bbb07818d53fed58f848e0182/layers/y-05.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b6f854ab7c835c2c55e7e9e2ab6471524e7dff527e30b139956a31819035920e/layers/y-09.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2dc0ebec1569270450ed7ff8ab3bd39431e7e2677323fb3cc8f8267a4a956a4a/layers/y-04.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6f3232d965a361b3e30daf51f650c1a80cb2252b2f552d10ae2f8db39b680d04/layers/y-07.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0ec70fbed41fd7ec7075b0e812a92d8407a84404ae661e2e4e135765ee886fb5/layers/y-11.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/52b59cfd081f33e8d466669d3163f18b1dd90b16b00157e2e4a0908228e2a084/layers/y-17.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9b1f4c8bbdb8b3470e3fd10064d79817895243b35a1fb602fd9e48dc76ef52da/layers/y-14.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4573521f262444a362732200e30d7fd237fa62265abef54198525e97bbcb7794/layers/y-15.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5843cf3cf18325a814cc278f8ea900c96fbeffbadab8d2fd0331a1d1029b4051/layers/y-19.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c2e078d4be49d638fd955d4b736cade4e5ddd2e86b1dc945a9e81d668860868e/layers/y-18.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/57d0614e8729b6aa65f26866bbf663ab8cd4e6f92864e93703747575e21427e8/layers/y-13.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4f55b1204884a4c7781ab26db1c78f60bbb5e8c8ac545fbad2cd7893fbce780e/layers/y-16.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/0254ea3f4a2f37882c286ae33643fdb6bc71c0edde6c09862e91a3b6620c0db1/layers/y-12.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/068c6b6c59a9711478b32830a5c0b4b57125051dbce126320c512c6c7a6fed91/layers/y-20.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5508cb54423a2d5cafc244aefc83d86ace0c03472b92066b39a1443c7d7ec154/layers/y-26.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/373df984de9b7ad0d6d95809a3299dfce704f2269ebb5435ac1665610218301d/layers/y-25.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2853242227d6a97b992006bf773fdac3bb74ecac08a7ef5f3bff28d151eae590/layers/y-23.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9961c67624b32d763a9f1df1cfdcd756daee1b2242d00bc1248e594cb6354f1b/layers/y-22.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9eb898dd4b7270594b4e518f97522b4170479c38dcb82dcab90bae4975d3ca74/layers/y-21.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f4e7a820e0c094c7de27b35bfa9d19623f394984c5e0185a6cc19331f909fd99/layers/y-27.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/743e0747fadff33bc77c4070d3a9b04fe15ecb46d500c96c5cdab9f47a89b6db/layers/y-24.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d82a866103e6874d481c56db887e626ff727024437f335d3fdeddcf08934c78a/layers/y-29.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/13797069fdc06f06d82fa454f5da8fcdc02bf32deff81c72b7d678c0e4804262/layers/y-28.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e3ed8ddb23297696a321333d21e78c8733569e1e143c79a0cfb37130e42aebd3/layers/y-30.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3/datasets/d4e0dfa5159d8e7f4bd1deca9d23df7546ab2d0a97693188a3b4e285c68251b3.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ddfc2751343e42bb8c63c4347218b1ee1934274f3b19dec1e2ac73bcc41f5d4c/cloud.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/595fac1eade3b55da32fe3a8047a751ec992e5eb850d39ab5a15f64dddca2aee/points.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/9193e89bf84c577cf77f8fb4d584505b03a069264f009959ece9df73c6c59d71/presentation.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/224e620caa689a95820bb15ad87bc37e7c7a6c2489db2136a441096246b10889/presentation.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d212db52a17a691903764ee360ea3cd30427b7290771f7a4581b00de1ce61b5d/provenance.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2db0a9aa6db465c0eb759ffa694dc4c68f8df98f8e68ac5fb7913e8f0426785d/provenance.json",
+ "status": 200
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/app-inspection.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/app-inspection.json
new file mode 100644
index 0000000000..e6ff190adf
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/app-inspection.json
@@ -0,0 +1,185 @@
+{
+ "objectId": "omega-centauri",
+ "base": "http://127.0.0.1:4219",
+ "browser": "148.0.7778.96",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "deviceScaleFactor": 1,
+ "revision": "0e22ec624e42986944cd914c7ddb88c84e3ef958",
+ "dirtyFiles": [
+ "M .agents/skills/nebula-lab/SKILL.md",
+ " M .github/workflows/universe.yml",
+ " M labs/nebula/docs/emission-compiler.md",
+ " M labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.ts",
+ " M labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.ts",
+ " M labs/nebula/packages/lab/src/adapters/preparation/css-volume.ts",
+ " M labs/nebula/packages/lab/src/server/workflows/compiler/bake.test.ts",
+ " M labs/nebula/packages/lab/src/server/workflows/compiler/bake.ts",
+ " M labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts",
+ " M labs/nebula/packages/volume-bake/src/compact-inputs/sampled.ts",
+ " M labs/nebula/packages/volume-bake/src/compiler/bake.ts",
+ " M labs/nebula/packages/volume-bake/src/compiler/compiler-sampling.test.ts",
+ " M labs/nebula/packages/volume-core/package.json",
+ " M labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts",
+ " M src/objects/omega-centauri/README.md",
+ " M src/objects/omega-centauri/object.json",
+ " M src/objects/omega-centauri/prepared-assets.json",
+ " M src/objects/omega-centauri/source/bake-inputs.json.gz",
+ " M src/objects/omega-centauri/source/delivery.json",
+ " M src/objects/omega-centauri/source/manifest.json",
+ " M src/objects/omega-centauri/source/presentation.json",
+ " M src/renderers/css/volume/prepared-volume-lenses.test.ts",
+ " M tests/objects/browser/omega-centauri/native-focus-browser.mts",
+ " M tools/nebula/application/backend.ts",
+ " M tools/nebula/application/objects.test.ts",
+ " M tools/nebula/application/objects.ts",
+ "?? labs/nebula/packages/volume-core/src/contracts/render-element-budget.test.ts",
+ "?? labs/nebula/packages/volume-core/src/contracts/render-element-budget.ts",
+ "?? src/objects/omega-centauri/evidence/2026-09-21/element-budget/",
+ "?? src/renderers/css/volume/compiler-render-budget.ts",
+ "?? tools/nebula/application/element-budget.cases.ts",
+ "?? tools/nebula/application/element-budget.test.ts",
+ "?? tools/nebula/application/element-budget.ts",
+ "?? tools/nebula/application/objects.cases.ts"
+ ],
+ "inputPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 433153,
+ "sha256": "42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/provenance.json",
+ "bytes": 76924,
+ "sha256": "974bcfa338298a83ac0190a5668bf4e4d377bb5fbe90e93e04b27afaf256ffd9"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/presentation.json",
+ "bytes": 4200,
+ "sha256": "69e809ee3e10109b51e9e3a2e8f159436a7038a43693e8820da396a90e0c53cc"
+ }
+ ],
+ "navigation": "direct native /sun/?focus= route",
+ "lenses": [
+ {
+ "id": "eso1119b",
+ "label": "VST · optical G / R / I",
+ "textures": 5,
+ "cameraRetained": true,
+ "sourceIds": [
+ "eso-eso1119b"
+ ]
+ },
+ {
+ "id": "eso0844a",
+ "label": "WFI · optical B / V / I",
+ "textures": 1,
+ "cameraRetained": true,
+ "sourceIds": [
+ "eso-eso0844a"
+ ]
+ }
+ ],
+ "stars": {
+ "total": 0,
+ "visibleWithStarsOn": 0,
+ "rootDisplayOn": "",
+ "rootDisplayOff": ""
+ },
+ "captures": [
+ {
+ "name": "lens-eso1119b",
+ "path": "lens-eso1119b.png",
+ "bytes": 368284,
+ "sha256": "2c945a4c09508dd9530233759e79de5fff97dac470a05711be4997353f48537f",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.369697426395,-0.684818648265,0.627970725357,0,0.929152201158,0.272480322918,-0.249861282927,0,0,0.675853454982,0.737036028556,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.017370865949,0.767816489802,-0.640434129591,0,0.885889099856,-0.28515750824,-0.365903839695,0,-0.463571706821,-0.573709677337,-0.675246952839,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.6462637826065765,\"distance\":69961698138109.18,\"distanceKilometers\":157007591595750180,\"distanceRadii\":225682897219.70703,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":0,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":179.99153509804657,\"silhouetteRadius\":0,\"bodyCenterKilometers\":[10329707871598.312,-20769413064916.992,157007589882229860]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNCosolIvrcoMKy48F86NT-Q4FubJGvw6NBQsczQAAAAL_TNG-ckHq_v8oOQfBFK1i_4L3efI6XFgABAAAAAAAAAAA",
+ "view": "earth",
+ "lens": "eso1119b",
+ "textures": 5,
+ "originalPath": "output/pr390-audit/element-budget/app-captures/lens-eso1119b.png"
+ },
+ {
+ "name": "lens-eso0844a",
+ "path": "lens-eso0844a.png",
+ "bytes": 387489,
+ "sha256": "18366727dbd7f82335187ea9c93923ff902e8ef920e425c65a579fa495df20ec",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.369697426395,-0.684818648265,0.627970725357,0,0.929152201158,0.272480322918,-0.249861282927,0,0,0.675853454982,0.737036028556,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.017370865949,0.767816489802,-0.640434129591,0,0.885889099856,-0.28515750824,-0.365903839695,0,-0.463571706821,-0.573709677337,-0.675246952839,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.6462637826065765,\"distance\":69961698138109.18,\"distanceKilometers\":157007591595750180,\"distanceRadii\":225682897219.70703,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":0,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":179.99153509804657,\"silhouetteRadius\":0,\"bodyCenterKilometers\":[10329707871598.312,-20769413064916.992,157007589882229860]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso0844a&v=UMNCosolIvrcoMKy48F86NT-Q4FubJGvw6NBQsczQAAAAL_TNG-ckHq_v8oOQfBFK1i_4L3efI6XFgABAAAAAAAAAAA",
+ "view": "earth",
+ "lens": "eso0844a",
+ "textures": 1,
+ "originalPath": "output/pr390-audit/element-budget/app-captures/lens-eso0844a.png"
+ },
+ {
+ "name": "view-earth",
+ "path": "view-earth.png",
+ "bytes": 368284,
+ "sha256": "2075f92769429698329d499590dc21f3edbd342ff6cefc552e3e03ec7e899ea4",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.369697426395,-0.684818648265,0.627970725357,0,0.929152201158,0.272480322918,-0.249861282927,0,0,0.675853454982,0.737036028556,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.017370865949,0.767816489802,-0.640434129591,0,0.885889099856,-0.28515750824,-0.365903839695,0,-0.463571706821,-0.573709677337,-0.675246952839,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.6462637826065765,\"distance\":69961698138109.18,\"distanceKilometers\":157007591595750180,\"distanceRadii\":225682897219.70703,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":0,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":179.99153509804657,\"silhouetteRadius\":0,\"bodyCenterKilometers\":[10329707871598.312,-20769413064916.992,157007589882229860]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNCosolIvrcoMKy48F86NT-Q4FubJGvw6NBQsczQAAAAL_TNG-ckHq_v8oOQfBFK1i_4L3efI6XFgABAAAAAAAAAAA",
+ "view": "earth (0°)",
+ "lens": "eso1119b",
+ "textures": 1,
+ "originalPath": "output/pr390-audit/element-budget/app-captures/view-earth.png"
+ },
+ {
+ "name": "view-oblique",
+ "path": "view-oblique.png",
+ "bytes": 389332,
+ "sha256": "e24faef4d0b98ba8c417d4908c7da7030caecd4ae7c8c2031af5ef6b8db0e6c1",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.659703396329,-0.684818648265,0.30953973551,0,0.62578011049,0.272480322918,-0.730858212609,0,0.416161846145,0.675853454982,0.608302083839,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.375953105308,0.767816489802,-0.51876476744,0,0.524550975902,-0.28515750824,-0.802204083029,0,-0.763875349308,-0.573709677337,-0.295553083902,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.39642396816377173,\"distance\":68455670452062.64,\"distanceKilometers\":153627773979032200,\"distanceRadii\":220824743393.75046,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":0,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":142.08246869895342,\"silhouetteRadius\":0,\"bodyCenterKilometers\":[94408353542680690,-20769413064933.375,121196350142008350]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNDdPZ-W4vbB8Ky48F86OVgQ3rpNwNknMJBQsczQAAAAL_cPfj3mMPjP6Ef-4phSgq_2k_88qH1IAABAAAAAAAAAAA",
+ "view": "oblique (34°)",
+ "lens": "eso1119b",
+ "textures": 1,
+ "originalPath": "output/pr390-audit/element-budget/app-captures/view-oblique.png"
+ },
+ {
+ "name": "view-side-plus",
+ "path": "view-side-plus.png",
+ "bytes": 414928,
+ "sha256": "1a583dae314679ab525cb7915b42c4e0977ae5e16898a4d4ed547de7cc4a3fcb",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.627970725357,-0.684818648265,-0.369697426395,0,-0.249861282927,0.272480322918,-0.929152201158,0,0.737036028556,0.675853454982,0,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.640434129591,0.767816489802,0.017370865949,0,-0.365903839695,-0.28515750824,-0.885889099856,0,-0.675246952839,-0.573709677337,0.463571706821,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.4533237230626572,\"distance\":74790239926811.3,\"distanceKilometers\":167843773926072960,\"distanceRadii\":241258838473.58484,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":1.1681049271654376e-7,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":84.92249396653635,\"silhouetteRadius\":5.840524635827188e-8,\"bodyCenterKilometers\":[167185134868234850,-20769413064916.992,-14854719568820442]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNDgo-v9HxgE8Ky48F86NT-w0pjJPWRnG1BQsczQAAAAL_ioMoqOXy5P9mwpgtBTDe_xDFgwHL_2QABAAAAAAAAAAA",
+ "view": "exact +90° side",
+ "lens": "eso1119b",
+ "textures": 2,
+ "originalPath": "output/pr390-audit/element-budget/app-captures/view-side-plus.png"
+ },
+ {
+ "name": "view-side-minus",
+ "path": "view-side-minus.png",
+ "bytes": 328742,
+ "sha256": "7717f9ad2eae93497df2d4ee0f3be5afd44577128ddac4eb83ea52693c8c9481",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(-0.627970725357,-0.684818648265,0.369697426395,0,0.249861282927,0.272480322918,0.929152201158,0,-0.737036028556,0.675853454982,0,0,0,0,0,1)\",\"skybox\":\"matrix3d(0.640434129591,0.767816489802,-0.017370865949,0,0.365903839695,-0.28515750824,0.885889099856,0,0.675246952839,-0.573709677337,-0.463571706821,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.45395489611826695,\"distance\":74789425752856.11,\"distanceKilometers\":167841946762135460,\"distanceRadii\":241256212105.98746,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":1.1697317484856256e-7,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":84.92951874427573,\"silhouetteRadius\":5.848658742428128e-8,\"bodyCenterKilometers\":[-167185134868234850,-20769413064933.375,-14834060153077260]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UEPDgo-v9HxgE8Ky48F86OVgw0pZv-MAHwZBQsczQAAAAL_EMWDAcv_YP-Kgyio5fLi_2bCmC0FMNQABAAAAAAAAAAA",
+ "view": "exact −90° side",
+ "lens": "eso1119b",
+ "textures": 2,
+ "originalPath": "output/pr390-audit/element-budget/app-captures/view-side-minus.png"
+ },
+ {
+ "name": "view-side-y",
+ "path": "view-side-y.png",
+ "bytes": 346817,
+ "sha256": "9c3f24ec147b78541513ec2e874d88eb5b3f6d546a663d5adaee53348a62eea6",
+ "camera": "{\"pose\":{\"schema\":\"cssearth-camera-pose@1\",\"scene\":\"matrix3d(0.369697426395,-0.627970725357,-0.684818648265,0,0.929152201158,0.249861282927,0.272480322918,0,0,-0.737036028556,0.675853454982,0,0,0,0,1)\",\"skybox\":\"matrix3d(-0.017370865949,0.640434129591,0.767816489802,0,0.885889099856,0.365903839695,-0.28515750824,0,-0.463571706821,0.675246952839,-0.573709677337,0,0,0,0,1)\",\"sunView\":\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\"},\"pitch\":143.76923076923077,\"controlPitch\":143.76923076923077,\"controlYaw\":0,\"zoom\":0.535181395928246,\"distance\":74707421314841.7,\"distanceKilometers\":167657912931404400,\"distanceRadii\":240991681660.7797,\"levelOfDetail\":{\"stage\":\"marker\",\"silhouetteDiameter\":1.3791044440937067e-7,\"billboardOpacity\":0,\"markerOpacity\":1,\"proxyOpacity\":1},\"focal\":1247.08,\"principalOffset\":[0,0],\"visibleRect\":{\"left\":-720,\"right\":720,\"top\":-500,\"bottom\":500},\"offAxisDegrees\":85.6961589587144,\"silhouetteRadius\":6.895522220468534e-8,\"bodyCenterKilometers\":[10329707871615.2,-167185134868234850,-12581984775926776]}",
+ "url": "http://127.0.0.1:4219/sun/?focus=omega-centauri&focusLens=eso1119b&v=UMNCosolIvr-ZsOCj6_0fGATw0ZZn86RufxBQsczQAAAAD_VUn7dgmC8P8ztrxvw-8q_4HG2k1oDJQABAAAAAAAAAAA",
+ "view": "exact local Y-axis side (90° about X)",
+ "lens": "eso1119b",
+ "textures": 3,
+ "originalPath": "output/pr390-audit/element-budget/app-captures/view-side-y.png"
+ }
+ ],
+ "errors": [],
+ "failed": [],
+ "result": "passed"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/cold-replay.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/cold-replay.json
new file mode 100644
index 0000000000..25ed960c8b
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/cold-replay.json
@@ -0,0 +1,1100 @@
+{
+ "status": "verified",
+ "verifiedAt": "2026-09-20T22:57:19.902Z",
+ "sandbox": "/private/tmp/cssearth-omega-budget-cold-yIxk9O",
+ "execution": {
+ "startedAt": "2026-09-20T22:53:14.121Z",
+ "endedAt": "2026-09-20T22:57:07.053Z",
+ "durationSeconds": 232.932,
+ "code": 0,
+ "reason": "",
+ "success": true,
+ "guardActive": true,
+ "sharp": {
+ "aom": "3.14.1",
+ "archive": "3.8.8",
+ "cairo": "1.18.4",
+ "cgif": "0.5.3",
+ "exif": "0.6.26",
+ "expat": "2.8.2",
+ "ffi": "3.6.0",
+ "fontconfig": "2.18.1",
+ "freetype": "2.14.3",
+ "fribidi": "1.0.16",
+ "glib": "2.89.1",
+ "harfbuzz": "14.2.1",
+ "heif": "1.23.1",
+ "highway": "1.4.0",
+ "imagequant": "2.4.1",
+ "lcms": "2.19.1",
+ "mozjpeg": "0826579",
+ "pango": "1.58.0",
+ "pixman": "0.46.4",
+ "png": "1.6.58",
+ "proxy-libintl": "0.5",
+ "rsvg": "2.62.90",
+ "tiff": "d01a94b",
+ "uhdr": "1acdbed",
+ "vips": "8.18.3",
+ "webp": "1.6.0",
+ "xml2": "2.15.3",
+ "zlib-ng": "2.3.3",
+ "sharp": "0.35.3"
+ }
+ },
+ "compactSha256": "c9f2535b13f4b4745179ec53b63240df1207ef7f38a4ea34dff76cba50a74d68",
+ "resultId": "59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e",
+ "sampling": {
+ "sliceCounts": {
+ "x": 49,
+ "y": 46,
+ "z": 56
+ },
+ "imageWidth": 512,
+ "samplesPerSlab": 4,
+ "layerPlan": {
+ "schema": "cssearth-volume-layer-plan@1",
+ "referenceSliceCounts": {
+ "x": 264,
+ "y": 264,
+ "z": 512
+ },
+ "referenceSamplesPerSlab": 4,
+ "axes": {
+ "x": [
+ {
+ "startCell": 0,
+ "endCell": 39
+ },
+ {
+ "startCell": 39,
+ "endCell": 46
+ },
+ {
+ "startCell": 46,
+ "endCell": 49
+ },
+ {
+ "startCell": 49,
+ "endCell": 56
+ },
+ {
+ "startCell": 56,
+ "endCell": 63
+ },
+ {
+ "startCell": 63,
+ "endCell": 70
+ },
+ {
+ "startCell": 70,
+ "endCell": 75
+ },
+ {
+ "startCell": 75,
+ "endCell": 80
+ },
+ {
+ "startCell": 80,
+ "endCell": 83
+ },
+ {
+ "startCell": 83,
+ "endCell": 86
+ },
+ {
+ "startCell": 86,
+ "endCell": 91
+ },
+ {
+ "startCell": 91,
+ "endCell": 95
+ },
+ {
+ "startCell": 95,
+ "endCell": 98
+ },
+ {
+ "startCell": 98,
+ "endCell": 101
+ },
+ {
+ "startCell": 101,
+ "endCell": 104
+ },
+ {
+ "startCell": 104,
+ "endCell": 107
+ },
+ {
+ "startCell": 107,
+ "endCell": 110
+ },
+ {
+ "startCell": 110,
+ "endCell": 113
+ },
+ {
+ "startCell": 113,
+ "endCell": 115
+ },
+ {
+ "startCell": 115,
+ "endCell": 118
+ },
+ {
+ "startCell": 118,
+ "endCell": 121
+ },
+ {
+ "startCell": 121,
+ "endCell": 124
+ },
+ {
+ "startCell": 124,
+ "endCell": 127
+ },
+ {
+ "startCell": 127,
+ "endCell": 130
+ },
+ {
+ "startCell": 130,
+ "endCell": 133
+ },
+ {
+ "startCell": 133,
+ "endCell": 135
+ },
+ {
+ "startCell": 135,
+ "endCell": 138
+ },
+ {
+ "startCell": 138,
+ "endCell": 141
+ },
+ {
+ "startCell": 141,
+ "endCell": 144
+ },
+ {
+ "startCell": 144,
+ "endCell": 147
+ },
+ {
+ "startCell": 147,
+ "endCell": 150
+ },
+ {
+ "startCell": 150,
+ "endCell": 153
+ },
+ {
+ "startCell": 153,
+ "endCell": 156
+ },
+ {
+ "startCell": 156,
+ "endCell": 159
+ },
+ {
+ "startCell": 159,
+ "endCell": 162
+ },
+ {
+ "startCell": 162,
+ "endCell": 165
+ },
+ {
+ "startCell": 165,
+ "endCell": 168
+ },
+ {
+ "startCell": 168,
+ "endCell": 171
+ },
+ {
+ "startCell": 171,
+ "endCell": 174
+ },
+ {
+ "startCell": 174,
+ "endCell": 178
+ },
+ {
+ "startCell": 178,
+ "endCell": 181
+ },
+ {
+ "startCell": 181,
+ "endCell": 186
+ },
+ {
+ "startCell": 186,
+ "endCell": 193
+ },
+ {
+ "startCell": 193,
+ "endCell": 200
+ },
+ {
+ "startCell": 200,
+ "endCell": 205
+ },
+ {
+ "startCell": 205,
+ "endCell": 211
+ },
+ {
+ "startCell": 211,
+ "endCell": 218
+ },
+ {
+ "startCell": 218,
+ "endCell": 225
+ },
+ {
+ "startCell": 225,
+ "endCell": 264
+ }
+ ],
+ "y": [
+ {
+ "startCell": 0,
+ "endCell": 39
+ },
+ {
+ "startCell": 39,
+ "endCell": 46
+ },
+ {
+ "startCell": 46,
+ "endCell": 51
+ },
+ {
+ "startCell": 51,
+ "endCell": 54
+ },
+ {
+ "startCell": 54,
+ "endCell": 59
+ },
+ {
+ "startCell": 59,
+ "endCell": 66
+ },
+ {
+ "startCell": 66,
+ "endCell": 73
+ },
+ {
+ "startCell": 73,
+ "endCell": 78
+ },
+ {
+ "startCell": 78,
+ "endCell": 83
+ },
+ {
+ "startCell": 83,
+ "endCell": 86
+ },
+ {
+ "startCell": 86,
+ "endCell": 91
+ },
+ {
+ "startCell": 91,
+ "endCell": 96
+ },
+ {
+ "startCell": 96,
+ "endCell": 101
+ },
+ {
+ "startCell": 101,
+ "endCell": 104
+ },
+ {
+ "startCell": 104,
+ "endCell": 107
+ },
+ {
+ "startCell": 107,
+ "endCell": 110
+ },
+ {
+ "startCell": 110,
+ "endCell": 113
+ },
+ {
+ "startCell": 113,
+ "endCell": 116
+ },
+ {
+ "startCell": 116,
+ "endCell": 119
+ },
+ {
+ "startCell": 119,
+ "endCell": 122
+ },
+ {
+ "startCell": 122,
+ "endCell": 125
+ },
+ {
+ "startCell": 125,
+ "endCell": 128
+ },
+ {
+ "startCell": 128,
+ "endCell": 131
+ },
+ {
+ "startCell": 131,
+ "endCell": 134
+ },
+ {
+ "startCell": 134,
+ "endCell": 137
+ },
+ {
+ "startCell": 137,
+ "endCell": 140
+ },
+ {
+ "startCell": 140,
+ "endCell": 143
+ },
+ {
+ "startCell": 143,
+ "endCell": 146
+ },
+ {
+ "startCell": 146,
+ "endCell": 149
+ },
+ {
+ "startCell": 149,
+ "endCell": 152
+ },
+ {
+ "startCell": 152,
+ "endCell": 155
+ },
+ {
+ "startCell": 155,
+ "endCell": 158
+ },
+ {
+ "startCell": 158,
+ "endCell": 161
+ },
+ {
+ "startCell": 161,
+ "endCell": 164
+ },
+ {
+ "startCell": 164,
+ "endCell": 169
+ },
+ {
+ "startCell": 169,
+ "endCell": 172
+ },
+ {
+ "startCell": 172,
+ "endCell": 175
+ },
+ {
+ "startCell": 175,
+ "endCell": 180
+ },
+ {
+ "startCell": 180,
+ "endCell": 187
+ },
+ {
+ "startCell": 187,
+ "endCell": 190
+ },
+ {
+ "startCell": 190,
+ "endCell": 195
+ },
+ {
+ "startCell": 195,
+ "endCell": 202
+ },
+ {
+ "startCell": 202,
+ "endCell": 209
+ },
+ {
+ "startCell": 209,
+ "endCell": 219
+ },
+ {
+ "startCell": 219,
+ "endCell": 228
+ },
+ {
+ "startCell": 228,
+ "endCell": 264
+ }
+ ],
+ "z": [
+ {
+ "startCell": 0,
+ "endCell": 102
+ },
+ {
+ "startCell": 102,
+ "endCell": 105
+ },
+ {
+ "startCell": 105,
+ "endCell": 138
+ },
+ {
+ "startCell": 138,
+ "endCell": 162
+ },
+ {
+ "startCell": 162,
+ "endCell": 177
+ },
+ {
+ "startCell": 177,
+ "endCell": 187
+ },
+ {
+ "startCell": 187,
+ "endCell": 192
+ },
+ {
+ "startCell": 192,
+ "endCell": 201
+ },
+ {
+ "startCell": 201,
+ "endCell": 210
+ },
+ {
+ "startCell": 210,
+ "endCell": 215
+ },
+ {
+ "startCell": 215,
+ "endCell": 219
+ },
+ {
+ "startCell": 219,
+ "endCell": 224
+ },
+ {
+ "startCell": 224,
+ "endCell": 229
+ },
+ {
+ "startCell": 229,
+ "endCell": 232
+ },
+ {
+ "startCell": 232,
+ "endCell": 237
+ },
+ {
+ "startCell": 237,
+ "endCell": 240
+ },
+ {
+ "startCell": 240,
+ "endCell": 243
+ },
+ {
+ "startCell": 243,
+ "endCell": 246
+ },
+ {
+ "startCell": 246,
+ "endCell": 249
+ },
+ {
+ "startCell": 249,
+ "endCell": 252
+ },
+ {
+ "startCell": 252,
+ "endCell": 255
+ },
+ {
+ "startCell": 255,
+ "endCell": 258
+ },
+ {
+ "startCell": 258,
+ "endCell": 261
+ },
+ {
+ "startCell": 261,
+ "endCell": 262
+ },
+ {
+ "startCell": 262,
+ "endCell": 265
+ },
+ {
+ "startCell": 265,
+ "endCell": 268
+ },
+ {
+ "startCell": 268,
+ "endCell": 271
+ },
+ {
+ "startCell": 271,
+ "endCell": 274
+ },
+ {
+ "startCell": 274,
+ "endCell": 275
+ },
+ {
+ "startCell": 275,
+ "endCell": 278
+ },
+ {
+ "startCell": 278,
+ "endCell": 279
+ },
+ {
+ "startCell": 279,
+ "endCell": 281
+ },
+ {
+ "startCell": 281,
+ "endCell": 284
+ },
+ {
+ "startCell": 284,
+ "endCell": 287
+ },
+ {
+ "startCell": 287,
+ "endCell": 290
+ },
+ {
+ "startCell": 290,
+ "endCell": 293
+ },
+ {
+ "startCell": 293,
+ "endCell": 296
+ },
+ {
+ "startCell": 296,
+ "endCell": 299
+ },
+ {
+ "startCell": 299,
+ "endCell": 302
+ },
+ {
+ "startCell": 302,
+ "endCell": 306
+ },
+ {
+ "startCell": 306,
+ "endCell": 309
+ },
+ {
+ "startCell": 309,
+ "endCell": 312
+ },
+ {
+ "startCell": 312,
+ "endCell": 317
+ },
+ {
+ "startCell": 317,
+ "endCell": 322
+ },
+ {
+ "startCell": 322,
+ "endCell": 329
+ },
+ {
+ "startCell": 329,
+ "endCell": 335
+ },
+ {
+ "startCell": 335,
+ "endCell": 342
+ },
+ {
+ "startCell": 342,
+ "endCell": 353
+ },
+ {
+ "startCell": 353,
+ "endCell": 361
+ },
+ {
+ "startCell": 361,
+ "endCell": 368
+ },
+ {
+ "startCell": 368,
+ "endCell": 381
+ },
+ {
+ "startCell": 381,
+ "endCell": 384
+ },
+ {
+ "startCell": 384,
+ "endCell": 405
+ },
+ {
+ "startCell": 405,
+ "endCell": 424
+ },
+ {
+ "startCell": 424,
+ "endCell": 473
+ },
+ {
+ "startCell": 473,
+ "endCell": 512
+ }
+ ]
+ }
+ },
+ "layerOptimization": {
+ "schema": "cssearth-layer-optimization@1",
+ "method": "gradient-weighted-depth-displacement@1",
+ "maximumLayers": 151,
+ "referenceLayers": 1040,
+ "plannedLayers": 151,
+ "probeWidth": 64,
+ "probeSubpixels": 2,
+ "targetNormalizedL1": 0.01,
+ "estimatedNormalizedL1": {
+ "x": 0.1065752005761363,
+ "y": 0.1046658764904314,
+ "z": 0.10729931793886195
+ },
+ "status": "budget-limited"
+ },
+ "renderBudget": {
+ "schema": "cssearth-render-element-budget@1",
+ "profile": {
+ "schema": "cssearth-render-element-profile@1",
+ "id": "css-volume-single-topology@1",
+ "maximumElements": 500,
+ "elementsPerSlab": 3,
+ "elementsPerStar": 1,
+ "reservedElements": 47
+ },
+ "starCount": 0,
+ "slabCount": 151,
+ "maximumSlabs": 151,
+ "totalElements": 500
+ }
+ },
+ "admission": {
+ "starCount": 0,
+ "slabCount": 149,
+ "impostorCount": 26,
+ "totalElements": 493
+ },
+ "lensCounts": [
+ {
+ "id": "eso1119b",
+ "axes": {
+ "x": 48,
+ "y": 45,
+ "z": 56
+ },
+ "stars": 0,
+ "resources": 29,
+ "impostors": 26
+ },
+ {
+ "id": "eso0844a",
+ "axes": {
+ "x": 48,
+ "y": 45,
+ "z": 56
+ },
+ "stars": 0,
+ "resources": 29,
+ "impostors": 26
+ }
+ ],
+ "banks": [
+ {
+ "id": "neutral",
+ "resources": 149,
+ "bytes": 2668542
+ },
+ {
+ "id": "eso1119b",
+ "resources": 149,
+ "bytes": 7347226
+ },
+ {
+ "id": "eso0844a",
+ "resources": 149,
+ "bytes": 6476494
+ }
+ ],
+ "verifiedExpectedSlabResources": 447,
+ "verifiedArtifacts": 59,
+ "artifacts": [
+ {
+ "filename": "eso0844a/atlases/x.webp",
+ "bytes": 630850,
+ "sha256": "2be14d24b7b8c9207982a44d0257d2d33e9e2f4c6f1a20e11e8e83a2d419ff3e"
+ },
+ {
+ "filename": "eso0844a/atlases/y.webp",
+ "bytes": 588746,
+ "sha256": "f378d5167d5c82e2779c3507a67f5f3cc7d3cd67e8396a70a5e8d47d0bca3247"
+ },
+ {
+ "filename": "eso0844a/atlases/z.webp",
+ "bytes": 619926,
+ "sha256": "f2cfe0f147d8dd07d66be1acb6442c9898e52a37426ed332818e704111ba2a6a"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00n.png",
+ "bytes": 12813,
+ "sha256": "4156c57dd44f146b3de1b146f31da37f676ef5ca97c388fc03d3442064c3fcae"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00p.png",
+ "bytes": 12843,
+ "sha256": "7827406e40fef2a444bf1da7c92dbb73aea22132dc4da142cbddaa1c3038b954"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0n0.png",
+ "bytes": 14310,
+ "sha256": "e9d6d2605bd483e58d3ff86bb49f010a0dbb3e88bd19a7529f56623288d5bb90"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0nn.png",
+ "bytes": 16252,
+ "sha256": "26161006830d714bee7e57dc84aa2bde9a22fb2d375d32188231d5b9fe6ca1d0"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0np.png",
+ "bytes": 16701,
+ "sha256": "089d623a473e4fa400428e882ed675afdfe55fa15d05c0dc05879dc5279f234c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0p0.png",
+ "bytes": 14252,
+ "sha256": "3612a4bb86230a87d514ae5417aa57300768b1a10d61f482414dfae1d4806da9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pn.png",
+ "bytes": 16654,
+ "sha256": "c8621057e346b5167a188ece82721e947bc5d06d358edc6883ab2ec933925309"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pp.png",
+ "bytes": 16266,
+ "sha256": "efbc1796cbeb321201318f8be76bf2f4bcb45491d99710180e1fc4566b271963"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n00.png",
+ "bytes": 13873,
+ "sha256": "aff1a64e71cdca04cbe72170feaa11e5284c57f2479703250981c7cd3510f1af"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0n.png",
+ "bytes": 16245,
+ "sha256": "1413954ca8edc01593679686edf534a55785207dd56ecbdbdd2a1f81bf1c6889"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0p.png",
+ "bytes": 16103,
+ "sha256": "aa614dd95fa6c33dc9650e91599b75a2ba7b634e884e43fc95f076dd198a850c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nn0.png",
+ "bytes": 17181,
+ "sha256": "8211b8045e0eddd5d15ba737cde7ccf4373cd19cd322b71c227e9db9ac29b85c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnn.png",
+ "bytes": 18333,
+ "sha256": "fd456e584c2d55ba70bb82c31ebdae54bd6050c52a1a5d77a1ae5a4e30be0d15"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnp.png",
+ "bytes": 18205,
+ "sha256": "4ac7ba2ab1c61280585304de0b1b343b2ce6d26177b2c830e4cca903d941dd21"
+ },
+ {
+ "filename": "eso0844a/impostors/view-np0.png",
+ "bytes": 16566,
+ "sha256": "f08f763fa3b91d78b5471c4c92f9c9eee005f730d0f135dd2ed3c946a3103072"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npn.png",
+ "bytes": 18179,
+ "sha256": "d4777b2ba664846faca602046e708e817d3e620617b3708029444f37520a979d"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npp.png",
+ "bytes": 17882,
+ "sha256": "1555e695cd4472ef5c8040e42f5af64b7adb30d5d48d9fd1640bd7611f490e0b"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p00.png",
+ "bytes": 13926,
+ "sha256": "4b3d0c279484f7fd46295037a60e41e081dd952ee23dfc4da54b2b3ea95c60c5"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0n.png",
+ "bytes": 16082,
+ "sha256": "4a8d2219e7753680c2a62ac94540737d38dace55b010179413c586a4d32a6dc9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0p.png",
+ "bytes": 16222,
+ "sha256": "42b8d608f6fce88844edebdc71820eb00a78bdb759eece0234be531ec242e189"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pn0.png",
+ "bytes": 16602,
+ "sha256": "05407bb1fe5bcafe2d1657f39c930055183bfd7aea3e7ec29cf1a4b6864f08e8"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnn.png",
+ "bytes": 17957,
+ "sha256": "5d3dff6ed46bdd9d02b524036cf15e54cb3235377e87f1092a9001425e5bac71"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnp.png",
+ "bytes": 18142,
+ "sha256": "04cc74f6a9e2120398b8c0d3804f175378e2dd50ca8e9e2c6c19f115a9bc81f4"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pp0.png",
+ "bytes": 17201,
+ "sha256": "e852ac472d7d640d6fbb134873e7cdd30d3ffbbc6a1560672c3c5d5ce9276fa9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppn.png",
+ "bytes": 18148,
+ "sha256": "394139a551defd9c14085f8e9177a1d106ec12de61724a855551462df3088259"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppp.png",
+ "bytes": 18310,
+ "sha256": "6e54542765e0daf109a7a93de5013efc0407eadd3c66139b7598598cff165349"
+ },
+ {
+ "filename": "eso1119b/atlases/x.webp",
+ "bytes": 717392,
+ "sha256": "7c140e5355dae4ee747b5033fbebbeb1e3065a60fbfab907c9a497c195c36ca8"
+ },
+ {
+ "filename": "eso1119b/atlases/y.webp",
+ "bytes": 665178,
+ "sha256": "b4303f0b696ebe2dcacc4d29cd16c076963e64ce9b83e6baaccc6d799c3d24d5"
+ },
+ {
+ "filename": "eso1119b/atlases/z.webp",
+ "bytes": 744376,
+ "sha256": "48cabfce2c49e49af9c1dcfc909279bda7c29e93e4c3dcee52bc4a1621281e62"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00n.png",
+ "bytes": 17819,
+ "sha256": "fc4b123e3af4cfd234ce5c9f0d0152f15332a11d856dc39b64d6b2ec99e33cff"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00p.png",
+ "bytes": 17808,
+ "sha256": "b3c41a3bc5e4b2e455a46a5f9b45a55f73818c9dd81492c0bf13f5c470a1c1cc"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0n0.png",
+ "bytes": 18194,
+ "sha256": "98795bd3eb6fff2b6725005a2d27ed77e956c314d0f5fbf8c49be78ffa42d1b6"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0nn.png",
+ "bytes": 20855,
+ "sha256": "3d3492f4abe86ed315bef89b4eed6ea12dde6e42a21127dde8dbd472afa4c09f"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0np.png",
+ "bytes": 21992,
+ "sha256": "d9becac304218245c0a81feb451e4938fa2738cee115795999110a1f3496608c"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0p0.png",
+ "bytes": 18135,
+ "sha256": "5c1c63b173cc7d66476ddb33bff80542a66b0ff5ae550aa54e1ce34c281fc2a3"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pn.png",
+ "bytes": 22003,
+ "sha256": "6b049f9dfddf5fcfa7b588776192479ff9e5f3dec6eba7e8b55729bcbcf11a77"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pp.png",
+ "bytes": 20827,
+ "sha256": "60e6c04a4bd0a75528ba788d6dfd0b765cf016d64eb317a30c277c329e0479fe"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n00.png",
+ "bytes": 18428,
+ "sha256": "bdbb5b335df7562f066a9b783f34af007123779136f787a6fe1e48d876df3f81"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0n.png",
+ "bytes": 21672,
+ "sha256": "b8374e6d60df50e5dfbd74b93e7afc720637052021a2c39498d7f679f0acc741"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0p.png",
+ "bytes": 21114,
+ "sha256": "17bafffbcf235dc9467ac0bc466d5190b43b376dda6a617fa83733d8ff52264d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nn0.png",
+ "bytes": 20813,
+ "sha256": "04574df58cc0371bcd9cbc504802abb32602736aca5ed78723c31e229c0fe3d2"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnn.png",
+ "bytes": 23385,
+ "sha256": "ba8db676f74fc73640634e80224852b89120b7458847d11e58ef6b4c6b58bb74"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnp.png",
+ "bytes": 23516,
+ "sha256": "59b45a749a76bc20b941251a563ab8f1ea41f6b25f6bb0d674308b09d597f729"
+ },
+ {
+ "filename": "eso1119b/impostors/view-np0.png",
+ "bytes": 20729,
+ "sha256": "644dc71d5c613fdbd1faf5ad662cc88572d8251308b5c62b0f9d8bc93c17d4fd"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npn.png",
+ "bytes": 24083,
+ "sha256": "2f256ea4a133442ce5dfdb564999c92e22825d3ecb4aa7964ebc120c4450c64c"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npp.png",
+ "bytes": 22900,
+ "sha256": "810e0f41aec1f714a15292a78560e1b8af8e21e4b6830b31ad5278d4092541a5"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p00.png",
+ "bytes": 18397,
+ "sha256": "eafbf2d83cf5d90187c429b8f2691aee84b8e8e1707f007398e6b38819982285"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0n.png",
+ "bytes": 21097,
+ "sha256": "50cef00922fc06c8b371031eb49d0b649b30973132272a2c1825cbe5a58536d8"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0p.png",
+ "bytes": 21643,
+ "sha256": "36898320964881289e223386a14a96f03c496db91828a226e2bee0f108c521a8"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pn0.png",
+ "bytes": 20712,
+ "sha256": "fc48f7402e52da24ceb72e20835c5f3721f2121d5cb346b1e9f57bd34258af7e"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnn.png",
+ "bytes": 22866,
+ "sha256": "b32298d848102bc899c2db9254ff43fb0d5716aaf074b7a3a003580ab6638610"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnp.png",
+ "bytes": 24053,
+ "sha256": "97fd71da3b4379b327435b54360bd950128998a28969d91576f19d64fb150b19"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pp0.png",
+ "bytes": 20832,
+ "sha256": "d155d4807b4efe66de898bc8863be49d9763c03629bbbf47a6b04c0fe82e29ed"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppn.png",
+ "bytes": 23507,
+ "sha256": "b16791ed70b6ca22bbd84ce8d9a295711c1b55cc8e1dd28dab6e968d535b9788"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppp.png",
+ "bytes": 23343,
+ "sha256": "cd8a0f7a94149a386a8b98ec8a47257539193ef9f612701309e32cc6def08212"
+ },
+ {
+ "filename": "lenses.json",
+ "bytes": 433153,
+ "sha256": "42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c"
+ }
+ ],
+ "excludedSeparateMetadata": [
+ "delivery.json",
+ "presentation.json",
+ "provenance.json"
+ ],
+ "knownMetadataDifferences": {
+ "objectPreparationSha256": {
+ "previous": "d922a8f326a20a02c40b2807de5c06a3d1179f502de439838716b722cf99d3b0",
+ "current": "d1e34564e98eae6ebe9d8d59b8cfd4ebe19c20e0c14f8279c72444f96faf36a5"
+ },
+ "implementationSha256": {
+ "previous": "0c77264e17c447f8e6ea2f38e7dcfef7c535b49e6981933e26d79f2a95a51927",
+ "current": "ce5b9ec3f018452fba8d1d56cb6f43552afbdfdb76b1435494900507c7261935"
+ },
+ "recipeSha256": {
+ "previous": "d922a8f326a20a02c40b2807de5c06a3d1179f502de439838716b722cf99d3b0",
+ "current": "d1e34564e98eae6ebe9d8d59b8cfd4ebe19c20e0c14f8279c72444f96faf36a5"
+ }
+ },
+ "sourceInputsUnchanged": true,
+ "deliveryReceipt": {
+ "schema": "cssearth-nebula-delivery-receipt@1",
+ "recipeSha256": "d1e34564e98eae6ebe9d8d59b8cfd4ebe19c20e0c14f8279c72444f96faf36a5",
+ "implementationSha256": "ce5b9ec3f018452fba8d1d56cb6f43552afbdfdb76b1435494900507c7261935",
+ "sourceResult": "59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e",
+ "acceptedLabResult": "59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e",
+ "renderElements": {
+ "starCount": 0,
+ "slabCount": 149,
+ "impostorCount": 26,
+ "totalElements": 493
+ },
+ "lenses": [
+ {
+ "id": "eso1119b",
+ "stars": 0,
+ "leaves": 29
+ },
+ {
+ "id": "eso0844a",
+ "stars": 0,
+ "leaves": 29
+ }
+ ]
+ },
+ "claim": "Ordinary isolated application replay verified compact resource digests/sizes and exact sampling in its immutable replay owner, then all generated app inventory bytes and final admission. This does not assert scientific or visual handoff acceptance."
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/delivery-guard.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/delivery-guard.json
new file mode 100644
index 0000000000..c0fa4e167c
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/delivery-guard.json
@@ -0,0 +1,71 @@
+{
+ "head": "0e22ec624e42986944cd914c7ddb88c84e3ef958",
+ "recordedAt": "2026-09-20T22:58:26.471435+00:00",
+ "sourceFiles": {
+ "tools/nebula/application/element-budget.ts": {
+ "bytes": 3096,
+ "sha256": "9d0678bee9988b51b2afbefe874ce92feabecc369a63403a58f03946af592a28"
+ },
+ "tools/nebula/application/element-budget.test.ts": {
+ "bytes": 1717,
+ "sha256": "500c59b9e5ce79dd9912a21be84c400f501c9222ad54fa3296bb6634bb43256b"
+ },
+ "tools/nebula/application/element-budget.cases.ts": {
+ "bytes": 6506,
+ "sha256": "435af09c9f369ccbc579006fb20fd0ef0bc18f6a5a466729175977e830ab223c"
+ },
+ "tools/nebula/application/objects.ts": {
+ "bytes": 23232,
+ "sha256": "3af766116ebc0436162b13db781b851b69b4060f470ff79e36a1cd3912bb04d6"
+ },
+ "tools/nebula/application/objects.test.ts": {
+ "bytes": 1696,
+ "sha256": "cfe387a0db16c48988f29a95ad32914638cc667fb03a02b227386aba52cc63c5"
+ },
+ "tools/nebula/application/objects.cases.ts": {
+ "bytes": 11547,
+ "sha256": "80ee489ed7724993d14aafa766e77b8fa3723af22cea00626011ee45f51e4fdb"
+ },
+ "src/renderers/css/volume/compiler-render-budget.ts": {
+ "bytes": 595,
+ "sha256": "68acdb48f890c5624a819a5a215d242284c8e9603d61aa8a5ca75306015c7b63"
+ },
+ "labs/nebula/packages/volume-core/src/contracts/render-element-budget.ts": {
+ "bytes": 4298,
+ "sha256": "ebde15faebea147d751ae75cfe62be549e1b81392a0982915dea8efc982cdc58"
+ }
+ },
+ "behavior": {
+ "unitPassed": 9,
+ "installerAndExistingPassed": 6,
+ "nativeWrappersPassed": 2
+ },
+ "mutation": {
+ "change": "remove objects.ts final admission call in isolated bundled current cases",
+ "pass": 5,
+ "fail": 1,
+ "expectedError": "Missing expected rejection."
+ },
+ "typecheck": {
+ "status": "pass",
+ "diagnostics": 0,
+ "log": "typecheck-stable.log",
+ "concurrentBuildFailureLog": "typecheck-final.log",
+ "reason": "Final rerun after root full build restored renderer declarations."
+ },
+ "exactCICommand": {
+ "outerPass": 22,
+ "outerFail": 0,
+ "unitInnerPass": 9,
+ "installerInnerPass": 6,
+ "log": "exact-ci-tests-final.log",
+ "priorFailureResolved": "Parent regenerated final Omega metadata; prior log preserved."
+ },
+ "updatedAt": "2026-09-20T22:59:51.480666+00:00",
+ "environment": {
+ "node": "v22.23.2",
+ "platform": "Darwin arm64",
+ "execution": "Local exact maintained CI command, not a GitHub run of unpushed changes."
+ },
+ "finalVerifiedAt": "2026-09-20T23:01:57.551119+00:00"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-after.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-after.png
new file mode 100644
index 0000000000..e48f9b41bd
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-after.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-before.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-before.png
new file mode 100644
index 0000000000..ca89c8fa69
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-before.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-diff.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-diff.png
new file mode 100644
index 0000000000..183284c66f
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso0844a-front-diff.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-after.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-after.png
new file mode 100644
index 0000000000..2c7ceaa851
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-after.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-before.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-before.png
new file mode 100644
index 0000000000..b00d6f7278
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-before.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-diff.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-diff.png
new file mode 100644
index 0000000000..d18913dbfa
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/eso1119b-front-diff.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/handoffs.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/handoffs.json
new file mode 100644
index 0000000000..a7f0ffb64b
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/handoffs.json
@@ -0,0 +1,176 @@
+{
+ "oldId": "7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549",
+ "optimizedId": "59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e",
+ "thresholds": {
+ "relativeLuminanceDifference": 0.05,
+ "normalizedL1": 0.04
+ },
+ "measurements": [
+ {
+ "version": "old",
+ "lens": "neutral",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.004804672942986058,
+ "normalizedL1": 0.12498652413707272,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "neutral",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.003411535415403307,
+ "normalizedL1": 0.1146546907233652,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "neutral",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.01257513269393465,
+ "normalizedL1": 0.12922000336543588,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso1119b",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.005002309945527142,
+ "normalizedL1": 0.12007917840015177,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso1119b",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.0033750817084131296,
+ "normalizedL1": 0.11017354402412256,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso1119b",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.0128238570542583,
+ "normalizedL1": 0.1240519478966761,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso0844a",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.004756726423958046,
+ "normalizedL1": 0.1238774224552733,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso0844a",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.0034671649169563516,
+ "normalizedL1": 0.11369655642048106,
+ "passed": false
+ },
+ {
+ "version": "old",
+ "lens": "eso0844a",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.013644155316165174,
+ "normalizedL1": 0.13234144636273965,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "neutral",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.008694965476756213,
+ "normalizedL1": 0.13188125127614667,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "neutral",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.019974440390013257,
+ "normalizedL1": 0.12519230604523487,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "neutral",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.016257896200268997,
+ "normalizedL1": 0.13483752492242546,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso1119b",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.008700856353693595,
+ "normalizedL1": 0.12227853298617221,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso1119b",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.020184772799704816,
+ "normalizedL1": 0.11574691531941868,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso1119b",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.01650064620759065,
+ "normalizedL1": 0.12534467725283968,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso0844a",
+ "seam": "xz",
+ "relativeLuminanceDifference": 0.006908970740758142,
+ "normalizedL1": 0.1291989641066443,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso0844a",
+ "seam": "yz",
+ "relativeLuminanceDifference": 0.01816507596202499,
+ "normalizedL1": 0.12207841477480044,
+ "passed": false
+ },
+ {
+ "version": "optimized",
+ "lens": "eso0844a",
+ "seam": "xy",
+ "relativeLuminanceDifference": 0.01630403321974954,
+ "normalizedL1": 0.1321377573141277,
+ "passed": false
+ }
+ ],
+ "frontChanges": [
+ {
+ "lens": "neutral",
+ "relativeLuminanceDifference": 0.19799067933951509,
+ "normalizedL1": 0.1116585069926175,
+ "passed": false
+ },
+ {
+ "lens": "eso1119b",
+ "relativeLuminanceDifference": 0.20024573870026677,
+ "normalizedL1": 0.11332034626392762,
+ "passed": false
+ },
+ {
+ "lens": "eso0844a",
+ "relativeLuminanceDifference": 0.1899583282996675,
+ "normalizedL1": 0.10968339408730671,
+ "passed": false
+ }
+ ],
+ "errors": [],
+ "failed": []
+}
\ No newline at end of file
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/implementation.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/implementation.json
new file mode 100644
index 0000000000..ca70e6356c
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/implementation.json
@@ -0,0 +1,139 @@
+{
+ "recordedAt": "2026-09-20T22:56:22.889199+00:00",
+ "round": "implementation1of3",
+ "status": "frozen-targeted-verified",
+ "scope": "Pure element budget + compiler admission + verified replay compatibility; renderer tests by pr380, application final admission by ci_analysis; no changes to scientific fit or optical copies",
+ "rule": "47 + separateStars + 3*plannedXYZ <= 500 for the renderer-owned single-topology delivery profile; zero-star allowance151",
+ "historicalCompatibility": "Saved missing-budget sampling only preserved by explicitly verified compact replay callers; saved new renderBudget always validated, including historical flag path",
+ "checks": [
+ {
+ "name": "targeted core/baker/direct dependents",
+ "passed": 31,
+ "failed": 0,
+ "log": "output/pr390-audit/renderer-budget-round1-final-targeted.log"
+ },
+ {
+ "name": "core+bake+Lab package types",
+ "passed": true,
+ "log": "output/pr390-audit/renderer-budget-round1-types.log"
+ },
+ {
+ "name": "root test types",
+ "files": 1758,
+ "passed": true,
+ "log": "output/pr390-audit/renderer-budget-round1-test-types.log"
+ },
+ {
+ "name": "narrow strict Nebula test types",
+ "rootFiles": 3,
+ "passed": true,
+ "config": "output/pr390-audit/render-element-budget/tsconfig.tests.json",
+ "log": "output/pr390-audit/renderer-budget-round1-nebula-test-types.log"
+ },
+ {
+ "name": "nebula dependency boundary",
+ "sentinel": "NEBULA_BOUNDARIES_OK",
+ "passed": true
+ },
+ {
+ "name": "documentation links",
+ "passed": true,
+ "log": "output/pr390-audit/renderer-budget-round1-docs.json"
+ }
+ ],
+ "mutation": "output/pr390-audit/render-element-budget/planner-mutation-receipt.json",
+ "review": "output/pr390-audit/render-element-budget/review/verified.md",
+ "sourcePins": [
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/render-element-budget.ts",
+ "bytes": 4298,
+ "sha256": "ebde15faebea147d751ae75cfe62be549e1b81392a0982915dea8efc982cdc58"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/render-element-budget.test.ts",
+ "bytes": 2042,
+ "sha256": "e4cade4f12c2f7e43c310be74c9b95a42f83d773ab46a7df2440d4eb8040282d"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts",
+ "bytes": 12864,
+ "sha256": "f85da67f5a3ea68a529ae0b449c9a83e991a9b37a926ec54604991a71cd1a4b8"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/package.json",
+ "bytes": 4100,
+ "sha256": "767f04fabfceab60608e9ee5fedf637392ee026b3ec5066ff9e59dbdb01a35af"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/bake.ts",
+ "bytes": 21310,
+ "sha256": "004bbf282306ea47aa37abed065f5118663d4de18783c51b5ecaa6c3aea24579"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/compiler-sampling.test.ts",
+ "bytes": 7824,
+ "sha256": "9c93e885ec7096aa60dedd3444af2e97887811b50fa85b9fb06bcbd26e521c21"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts",
+ "bytes": 8788,
+ "sha256": "2b788e4cb4f2b7b85ac6e9134457f540db4940d7f929e06beef82546a4c8be20"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compact-inputs/sampled.ts",
+ "bytes": 12102,
+ "sha256": "c5c733bcc6225cef08279c6a876bd8391181951b598069a2834ca6b88cefc745"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/bake.ts",
+ "bytes": 824,
+ "sha256": "1b7a185027c89f8f3bd529e6c791eec551b047881ed2995d468b313b6323da12"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/bake.test.ts",
+ "bytes": 6532,
+ "sha256": "ce026d64be3724ec2af7c3e18d0385084aa84d425f3da9f677e71ab26c5b652c"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/preparation/css-volume.ts",
+ "bytes": 407,
+ "sha256": "ff2e403521e695afd4b1ced22b177e888c8220024e30c4a8ff3117fe2c4a3616"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.ts",
+ "bytes": 1362,
+ "sha256": "ceae17acf80fe487e1a5e5923d38a5671e5409ae986a5b80b2319aa62a002067"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.ts",
+ "bytes": 7348,
+ "sha256": "efa3fa10cac440fe2a4dd8a38283315a7fe58410e498c2a98c22f659385c56ee"
+ },
+ {
+ "path": "src/renderers/css/volume/compiler-render-budget.ts",
+ "bytes": 595,
+ "sha256": "68acdb48f890c5624a819a5a215d242284c8e9603d61aa8a5ca75306015c7b63"
+ },
+ {
+ "path": "tools/nebula/application/backend.ts",
+ "bytes": 974,
+ "sha256": "b70550d2f3db3d5a41125425cbf0556f50468c68fd765cc7b06f2dcb5e925d4f"
+ },
+ {
+ "path": ".agents/skills/nebula-lab/SKILL.md",
+ "bytes": 34046,
+ "sha256": "c351ad535966dfb39d2e4621940317f4880055b80450e0688141f4a4183cadfd"
+ },
+ {
+ "path": "labs/nebula/docs/emission-compiler.md",
+ "bytes": 15405,
+ "sha256": "ef7a14c75d14d5a01d0bfd1b753ea25a87faa2a130e559be52f9d3e7b6d28155"
+ },
+ {
+ "path": ".github/workflows/universe.yml",
+ "bytes": 36948,
+ "sha256": "80927298ee9103f682640c41e6dab4052a839f43a6516f2293fdf8c577ecf5f1"
+ }
+ ],
+ "remaining": "Actual new Omega application cold replay and parent-owned real Lab/app counts. Scientific handoff remains failed and is not reclassified by quota validation."
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lab-browser.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lab-browser.json
new file mode 100644
index 0000000000..3da3830d94
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lab-browser.json
@@ -0,0 +1,73 @@
+{
+ "id": "59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e",
+ "base": "http://127.0.0.1:4340",
+ "errors": [],
+ "blocked": [],
+ "states": [
+ {
+ "stage": "initial",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso1119b",
+ "material": "textured",
+ "pose": "0,0",
+ "stars": "true"
+ },
+ {
+ "stage": "lens eso1119b",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso1119b",
+ "material": "textured",
+ "pose": "0,0",
+ "stars": "true"
+ },
+ {
+ "stage": "lens eso0844a",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "0,0",
+ "stars": "true"
+ },
+ {
+ "stage": "before orbit",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "0,0",
+ "stars": "true"
+ },
+ {
+ "stage": "after orbit",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "35,-17.5",
+ "stars": "true"
+ },
+ {
+ "stage": "zoom",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "35,-17.5",
+ "stars": "true"
+ },
+ {
+ "stage": "refresh retained",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "35,-17.5",
+ "stars": "true"
+ }
+ ],
+ "result": "passed",
+ "browser": "148.0.7778.96"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lab-suite.tap b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lab-suite.tap
new file mode 100644
index 0000000000..06ebcbf32f
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lab-suite.tap
@@ -0,0 +1,4225 @@
+NEBULA_TEST_DISCOVERY 195 test files
+TAP version 13
+# Subtest: public camera helper drives pointer rotation and Earth restoration without preset controls
+ok 1 - public camera helper drives pointer rotation and Earth restoration without preset controls
+ ---
+ duration_ms: 1378.218167
+ type: 'test'
+ ...
+# Subtest: display guard blocks real writes before the server receives them; explicit disposal restores transport
+ok 2 - display guard blocks real writes before the server receives them; explicit disposal restores transport
+ ---
+ duration_ms: 207.892
+ type: 'test'
+ ...
+# Subtest: retention probe rejects same-count node replacement and complete scene remounts
+ok 3 - retention probe rejects same-count node replacement and complete scene remounts
+ ---
+ duration_ms: 290.318791
+ type: 'test'
+ ...
+# Subtest: decode probe holds a real bitmap until release and restores the original browser decoder
+ok 4 - decode probe holds a real bitmap until release and restores the original browser decoder
+ ---
+ duration_ms: 660.647833
+ type: 'test'
+ ...
+# Subtest: posterior median distances produce physical fore/background, independently of image bounds
+ok 5 - posterior median distances produce physical fore/background, independently of image bounds
+ ---
+ duration_ms: 45.426167
+ type: 'test'
+ ...
+# Subtest: world-to-local transform uses the actual rotated, recentered frame without a second image reflection
+ok 6 - world-to-local transform uses the actual rotated, recentered frame without a second image reflection
+ ---
+ duration_ms: 7.235333
+ type: 'test'
+ ...
+# Subtest: proper motion uses Gaia cos-dec tangent components at the pole and the frame epoch
+ok 7 - proper motion uses Gaia cos-dec tangent components at the pole and the frame epoch
+ ---
+ duration_ms: 9.489958
+ type: 'test'
+ ...
+# Subtest: the outer boundary is a smooth sphere with faint taper, not a rectangular image footprint
+ok 8 - the outer boundary is a smooth sphere with faint taper, not a rectangular image footprint
+ ---
+ duration_ms: 3.771375
+ type: 'test'
+ ...
+# Subtest: measured G determines display area and the physical footprint scales with source distance
+ok 9 - measured G determines display area and the physical footprint scales with source distance
+ ---
+ duration_ms: 5.465166
+ type: 'test'
+ ...
+# Subtest: Cardiel coefficients yield independently calculated zero-color fluxes and neutral out-of-domain fallback
+ok 10 - Cardiel coefficients yield independently calculated zero-color fluxes and neutral out-of-domain fallback
+ ---
+ duration_ms: 3.997042
+ type: 'test'
+ ...
+# Subtest: brightness budget is deterministic, includes explicit retained stars, and discards old image fields
+ok 11 - brightness budget is deterministic, includes explicit retained stars, and discards old image fields
+ ---
+ duration_ms: 42.128584
+ type: 'test'
+ ...
+# Subtest: angular deduplication uses retained world directions and preserves their original depth
+ok 12 - angular deduplication uses retained world directions and preserves their original depth
+ ---
+ duration_ms: 15.65175
+ type: 'test'
+ ...
+# Subtest: a retained core outside the selected image keeps its detecting-source light without moving
+ok 13 - a retained core outside the selected image keeps its detecting-source light without moving
+ ---
+ duration_ms: 5.235792
+ type: 'test'
+ ...
+# Subtest: source pins reject content drift, traversal and escaping symlinks
+ok 14 - source pins reject content drift, traversal and escaping symlinks
+ ---
+ duration_ms: 3.126458
+ type: 'test'
+ ...
+# Subtest: runtime validation rejects lossy identifiers, malformed uncertainty, duplicate identities and unsupported frames
+ok 15 - runtime validation rejects lossy identifiers, malformed uncertainty, duplicate identities and unsupported frames
+ ---
+ duration_ms: 18.975291
+ type: 'test'
+ ...
+# Subtest: the acquired Crab catalogue prepares a nonempty physical volume with pinned uncertainty and no image stars
+ok 16 - the acquired Crab catalogue prepares a nonempty physical volume with pinned uncertainty and no image stars
+ ---
+ duration_ms: 3.108208
+ type: 'test'
+ ...
+# Subtest: the checked-in Helix selection retains wide-image cores and its central star across the budget and lenses
+ok 17 - the checked-in Helix selection retains wide-image cores and its central star across the budget and lenses
+ ---
+ duration_ms: 158.569042
+ type: 'test'
+ ...
+# Subtest: catalogue refresh preserves the existing Helix core identities, matching tolerance and evidence
+ok 18 - catalogue refresh preserves the existing Helix core identities, matching tolerance and evidence
+ ---
+ duration_ms: 99.089833
+ type: 'test'
+ ...
+# Subtest: cold parity rejects independent field, frame, star, lens and alpha mutations
+ok 19 - cold parity rejects independent field, frame, star, lens and alpha mutations
+ ---
+ duration_ms: 73.736791
+ type: 'test'
+ ...
+# Subtest: compact compiler validates the accepted field, material correspondence and sampling
+ok 20 - compact compiler validates the accepted field, material correspondence and sampling
+ ---
+ duration_ms: 64.998541
+ type: 'test'
+ ...
+# Subtest: compact replay cannot silently omit a retained density envelope or its colors
+ok 21 - compact replay cannot silently omit a retained density envelope or its colors
+ ---
+ duration_ms: 41.230667
+ type: 'test'
+ ...
+# Subtest: compact sampling keeps historical omission and rejects a grouped plan that differs from saved output counts
+ok 22 - compact sampling keeps historical omission and rejects a grouped plan that differs from saved output counts
+ ---
+ duration_ms: 81.06675
+ type: 'test'
+ ...
+# Subtest: compact sampled replay rejects changed measured particles before any reconstruction
+ok 23 - compact sampled replay rejects changed measured particles before any reconstruction
+ ---
+ duration_ms: 25.550291
+ type: 'test'
+ ...
+# Subtest: sampled planning preserves features unique to any lens without changing its emission
+ok 24 - sampled planning preserves features unique to any lens without changing its emission
+ ---
+ duration_ms: 0.6755
+ type: 'test'
+ ...
+# Subtest: retained sampled loader rejects a renamed scene before reading particles or writing outputs
+ok 25 - retained sampled loader rejects a renamed scene before reading particles or writing outputs
+ ---
+ duration_ms: 40.595667
+ type: 'test'
+ ...
+# PHOTO_MASTER_MASTER 4/144 X-3 192px
+# PHOTO_MASTER_MASTER 8/144 X-7 192px
+# PHOTO_MASTER_MASTER 12/144 X-11 192px
+# PHOTO_MASTER_MASTER 16/144 X-15 192px
+# PHOTO_MASTER_MASTER 20/144 X-19 192px
+# PHOTO_MASTER_MASTER 24/144 X-23 192px
+# PHOTO_MASTER_MASTER 28/144 X-27 192px
+# PHOTO_MASTER_MASTER 32/144 X-31 192px
+# PHOTO_MASTER_MASTER 36/144 X-35 192px
+# PHOTO_MASTER_MASTER 40/144 X-39 192px
+# PHOTO_MASTER_MASTER 44/144 X-43 192px
+# PHOTO_MASTER_MASTER 48/144 X-47 192px
+# PHOTO_MASTER_MASTER 52/144 Y-3 192px
+# PHOTO_MASTER_MASTER 56/144 Y-7 192px
+# PHOTO_MASTER_MASTER 60/144 Y-11 192px
+# PHOTO_MASTER_MASTER 64/144 Y-15 192px
+# PHOTO_MASTER_MASTER 68/144 Y-19 192px
+# PHOTO_MASTER_MASTER 72/144 Y-23 192px
+# PHOTO_MASTER_MASTER 76/144 Y-27 192px
+# PHOTO_MASTER_MASTER 80/144 Y-31 192px
+# PHOTO_MASTER_MASTER 84/144 Y-35 192px
+# PHOTO_MASTER_MASTER 88/144 Y-39 192px
+# PHOTO_MASTER_MASTER 92/144 Y-43 192px
+# PHOTO_MASTER_MASTER 96/144 Y-47 192px
+# PHOTO_MASTER_MASTER 100/144 Z-3 192px
+# PHOTO_MASTER_MASTER 104/144 Z-7 192px
+# PHOTO_MASTER_MASTER 108/144 Z-11 192px
+# PHOTO_MASTER_MASTER 112/144 Z-15 192px
+# PHOTO_MASTER_MASTER 116/144 Z-19 192px
+# PHOTO_MASTER_MASTER 120/144 Z-23 192px
+# PHOTO_MASTER_MASTER 124/144 Z-27 192px
+# PHOTO_MASTER_MASTER 128/144 Z-31 192px
+# PHOTO_MASTER_MASTER 132/144 Z-35 192px
+# PHOTO_MASTER_MASTER 136/144 Z-39 192px
+# PHOTO_MASTER_MASTER 140/144 Z-43 192px
+# PHOTO_MASTER_MASTER 144/144 Z-47 192px
+# Subtest: retained symmetry fields replay accepted slices without source photographs
+ok 26 - retained symmetry fields replay accepted slices without source photographs
+ ---
+ duration_ms: 2964.984083
+ type: 'test'
+ ...
+# Subtest: source west/north/away lands in ICRS without mirroring measured sky offsets or depth
+ok 27 - source west/north/away lands in ICRS without mirroring measured sky offsets or depth
+ ---
+ duration_ms: 0.9215
+ type: 'test'
+ ...
+# Subtest: prepared CSS reflection follows PolyCSS physical XY permutation and leaves image bytes intact
+ok 28 - prepared CSS reflection follows PolyCSS physical XY permutation and leaves image bytes intact
+ ---
+ duration_ms: 1.053583
+ type: 'test'
+ ...
+# Subtest: invalid physical frames cannot silently assign units or a sky direction
+ok 29 - invalid physical frames cannot silently assign units or a sky direction
+ ---
+ duration_ms: 0.217875
+ type: 'test'
+ ...
+# Subtest: new physical compiler transport and historical source transport produce identical app sky geometry
+ok 30 - new physical compiler transport and historical source transport produce identical app sky geometry
+ ---
+ duration_ms: 4.802167
+ type: 'test'
+ ...
+# Subtest: a pinned optical composite is a compiler delivery stage, never a symmetry fallback
+ok 31 - a pinned optical composite is a compiler delivery stage, never a symmetry fallback
+ ---
+ duration_ms: 4.021541
+ type: 'test'
+ ...
+# Subtest: delivery restores missing impostors, rejects drift and rebuilds when their generator changes
+not ok 32 - delivery restores missing impostors, rejects drift and rebuilds when their generator changes
+ ---
+ duration_ms: 180.614917
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-objects.test.mjs:15374:1'
+ failureType: 'testCodeFailure'
+ error: |-
+ Cannot find module '@cssearth/volume-bake/package.json'
+ Require stack:
+ - /var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/nebula-impostor-delivery-4P7j0T/package.json
+ code: 'MODULE_NOT_FOUND'
+ stack: |-
+ Function._resolveFilename (node:internal/modules/cjs/loader:1430:15)
+ Function.resolve (node:internal/modules/helpers:157:19)
+ packageImplementationPins (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-objects.test.mjs:3897:35)
+ prepareNebulaObject (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-objects.test.mjs:8627:29)
+ async TestContext. (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/adapters/application/delivery/nebula-objects.test.mjs:15378:20)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: cutoff zero preserves every kept alpha and smooth cutoff distinguishes centre from edge
+ok 33 - cutoff zero preserves every kept alpha and smooth cutoff distinguishes centre from edge
+ ---
+ duration_ms: 0.493875
+ type: 'test'
+ ...
+# Subtest: non-square texture texels retain each prepared X/Y/Z leaf plane and centre
+ok 34 - non-square texture texels retain each prepared X/Y/Z leaf plane and centre
+ ---
+ duration_ms: 0.310334
+ type: 'test'
+ ...
+# Subtest: integrated signal is globally normalized, brighter at centre, and invariant along an Earth ray
+ok 35 - integrated signal is globally normalized, brighter at centre, and invariant along an Earth ray
+ ---
+ duration_ms: 0.25225
+ type: 'test'
+ ...
+# Subtest: kept and removed optical weights partition a nonzero source alpha
+ok 36 - kept and removed optical weights partition a nonzero source alpha
+ ---
+ duration_ms: 0.071667
+ type: 'test'
+ ...
+# Subtest: RGBA filtering preserves geometry-sized RGB and partitions alpha without invented glow
+ok 37 - RGBA filtering preserves geometry-sized RGB and partitions alpha without invented glow
+ ---
+ duration_ms: 0.451917
+ type: 'test'
+ ...
+# Subtest: tangent photo mapping matches independent Astropy rays and holds those rays across physical depths
+ok 38 - tangent photo mapping matches independent Astropy rays and holds those rays across physical depths
+ ---
+ duration_ms: 5.907125
+ type: 'test'
+ ...
+# Subtest: prior samples perspective rays, decodes after interpolation, retains full depth and never mutates source
+ok 39 - prior samples perspective rays, decodes after interpolation, retains full depth and never mutates source
+ ---
+ duration_ms: 0.860167
+ type: 'test'
+ ...
+# Subtest: every prepared image maps its full raster edges to the calibrated provenance corners without bleed
+ok 40 - every prepared image maps its full raster edges to the calibrated provenance corners without bleed
+ ---
+ duration_ms: 16.731625
+ type: 'test'
+ ...
+# Subtest: full-precision TAN projective interior maps independently calculated Astropy ICRS rays
+ok 41 - full-precision TAN projective interior maps independently calculated Astropy ICRS rays
+ ---
+ duration_ms: 14.196041
+ type: 'test'
+ ...
+# Subtest: old real Tarantula leaf fails calibrated edges even with atlas seamBleed zero
+ok 42 - old real Tarantula leaf fails calibrated edges even with atlas seamBleed zero
+ ---
+ duration_ms: 3.72475
+ type: 'test'
+ ...
+# Subtest: sky compiler rejects invalid dimensions, non-coplanar, degenerate and crossed quads
+ok 43 - sky compiler rejects invalid dimensions, non-coplanar, degenerate and crossed quads
+ ---
+ duration_ms: 0.291375
+ type: 'test'
+ ...
+# Subtest: horizontal orbit keeps one sign at every pose, including the Earth-view pose (eastLeft true)
+ok 44 - horizontal orbit keeps one sign at every pose, including the Earth-view pose (eastLeft true)
+ ---
+ duration_ms: 6.461125
+ type: 'test'
+ ...
+# Subtest: horizontal orbit keeps one sign at every pose, including the Earth-view pose (eastLeft false)
+ok 45 - horizontal orbit keeps one sign at every pose, including the Earth-view pose (eastLeft false)
+ ---
+ duration_ms: 0.898709
+ type: 'test'
+ ...
+# Subtest: Earth projection matches the source pixel edges, handedness, pan, zoom and resized fit
+ok 46 - Earth projection matches the source pixel edges, handedness, pan, zoom and resized fit
+ ---
+ duration_ms: 5.464375
+ type: 'test'
+ ...
+# Subtest: posed cloud stays centered, camera orientation is proper, and ordinary-depth parallax is below 0.05 pixels
+ok 47 - posed cloud stays centered, camera orientation is proper, and ordinary-depth parallax is below 0.05 pixels
+ ---
+ duration_ms: 0.627708
+ type: 'test'
+ ...
+# Subtest: prepared compact lights and slab geometry share the same physical camera projection
+ok 48 - prepared compact lights and slab geometry share the same physical camera projection
+ ---
+ duration_ms: 0.284416
+ type: 'test'
+ ...
+# Subtest: true orthographic CSS transport keeps exact image projection at every depth without large CSS translations
+ok 49 - true orthographic CSS transport keeps exact image projection at every depth without large CSS translations
+ ---
+ duration_ms: 0.432083
+ type: 'test'
+ ...
+# Subtest: negative source-away depth is nearer and magnifies more than positive source-away depth
+ok 50 - negative source-away depth is nearer and magnifies more than positive source-away depth
+ ---
+ duration_ms: 0.114292
+ type: 'test'
+ ...
+# Subtest: historical compiler banks transport retained source depth once and agree with their stars
+ok 51 - historical compiler banks transport retained source depth once and agree with their stars
+ ---
+ duration_ms: 0.825625
+ type: 'test'
+ ...
+# Subtest: legacy Shapes and Joint-fit share the angular-to-physical mount and camera boundary
+ok 52 - legacy Shapes and Joint-fit share the angular-to-physical mount and camera boundary
+ ---
+ duration_ms: 1.697625
+ type: 'test'
+ ...
+# Subtest: tone bank swaps only after every prepared image decodes, without replacing leaves
+ok 53 - tone bank swaps only after every prepared image decodes, without replacing leaves
+ ---
+ duration_ms: 0.60025
+ type: 'test'
+ ...
+# Subtest: stale tone decode cannot overwrite a newer selection or a new mount
+ok 54 - stale tone decode cannot overwrite a newer selection or a new mount
+ ---
+ duration_ms: 0.114792
+ type: 'test'
+ ...
+# Subtest: prepared tone selected before an overlay mounts reaches its later retained nodes
+ok 55 - prepared tone selected before an overlay mounts reaches its later retained nodes
+ ---
+ duration_ms: 1.662167
+ type: 'test'
+ ...
+# Subtest: a retained leaf rebound to another image layer is no longer changed by its old tone resource
+ok 56 - a retained leaf rebound to another image layer is no longer changed by its old tone resource
+ ---
+ duration_ms: 0.379542
+ type: 'test'
+ ...
+# Subtest: bundles preserve imported source URLs and text while only the real entry runs its guard
+ok 57 - bundles preserve imported source URLs and text while only the real entry runs its guard
+ ---
+ duration_ms: 129.6475
+ type: 'test'
+ ...
+# Subtest: DataLink preserves matching identity, units and compound science links
+ok 58 - DataLink preserves matching identity, units and compound science links
+ ---
+ duration_ms: 2.221416
+ type: 'test'
+ ...
+# Subtest: enrichment never GETs science pixels; validates preview and records original discovery
+ok 59 - enrichment never GETs science pixels; validates preview and records original discovery
+ ---
+ duration_ms: 29.034542
+ type: 'test'
+ ...
+# Subtest: compound datasets and unverified previews remain unresolved rather than selecting arbitrary files
+ok 60 - compound datasets and unverified previews remain unresolved rather than selecting arbitrary files
+ ---
+ duration_ms: 0.575875
+ type: 'test'
+ ...
+# Subtest: MAST uses returned jpegURI, keeps file identity and measures this file with HEAD
+ok 61 - MAST uses returned jpegURI, keeps file identity and measures this file with HEAD
+ ---
+ duration_ms: 0.781833
+ type: 'test'
+ ...
+# Subtest: metadata MIME/length limits cannot fall through to a science download
+ok 62 - metadata MIME/length limits cannot fall through to a science download
+ ---
+ duration_ms: 0.351166
+ type: 'test'
+ ...
+# Subtest: selection covers distinct bands before repeats, caps at 12 and excludes association/catalogue products
+ok 63 - selection covers distinct bands before repeats, caps at 12 and excludes association/catalogue products
+ ---
+ duration_ms: 5.851708
+ type: 'test'
+ ...
+# Subtest: overlap copies keep their own identity and metadata while shared access updates avoid double counting
+ok 64 - overlap copies keep their own identity and metadata while shared access updates avoid double counting
+ ---
+ duration_ms: 0.282833
+ type: 'test'
+ ...
+# Subtest: finite XYZ material repaint preserves geometry and every decoded alpha byte across all banks
+ok 65 - finite XYZ material repaint preserves geometry and every decoded alpha byte across all banks
+ ---
+ duration_ms: 32.985666
+ type: 'test'
+ ...
+# Subtest: explicit settings validation and cached artifact corruption fail closed
+ok 66 - explicit settings validation and cached artifact corruption fail closed
+ ---
+ duration_ms: 6.409584
+ type: 'test'
+ ...
+# Subtest: source unpacking retains physical weights and rejects malformed points
+ok 67 - source unpacking retains physical weights and rejects malformed points
+ ---
+ duration_ms: 1.386708
+ type: 'test'
+ ...
+# Subtest: comparison hypothesis and bounded sampling are deterministic and independent of observations
+ok 68 - comparison hypothesis and bounded sampling are deterministic and independent of observations
+ ---
+ duration_ms: 0.486792
+ type: 'test'
+ ...
+# Subtest: monotone pooling never lets a bright bin map below a fainter one
+ok 69 - monotone pooling never lets a bright bin map below a fainter one
+ ---
+ duration_ms: 0.662042
+ type: 'test'
+ ...
+# Subtest: the footprint splits into two disjoint checkerboard halves
+ok 70 - the footprint splits into two disjoint checkerboard halves
+ ---
+ duration_ms: 0.505166
+ type: 'test'
+ ...
+# Subtest: one fit step from paired pixels moves a mid-tone bend back onto the target and held-out follows
+ok 71 - one fit step from paired pixels moves a mid-tone bend back onto the target and held-out follows
+ ---
+ duration_ms: 22.509916
+ type: 'test'
+ ...
+# Subtest: the objective sees a core that plateaus even when p50 and p90 match
+ok 72 - the objective sees a core that plateaus even when p50 and p90 match
+ ---
+ duration_ms: 69.979
+ type: 'test'
+ ...
+# Subtest: re-exposure from the byte recovers the projection at another exposure
+ok 73 - re-exposure from the byte recovers the projection at another exposure
+ ---
+ duration_ms: 5.516416
+ type: 'test'
+ ...
+# Subtest: exposure is solved jointly with the curve when the core rides the opacity shoulder
+ok 74 - exposure is solved jointly with the curve when the core rides the opacity shoulder
+ ---
+ duration_ms: 2092.599833
+ type: 'test'
+ ...
+# Subtest: full-particle transform preserves every record and grid planning pads beyond smoothing support
+ok 75 - full-particle transform preserves every record and grid planning pads beyond smoothing support
+ ---
+ duration_ms: 0.748416
+ type: 'test'
+ ...
+# Subtest: delivery rejects failed proof, changed registration and changed prepared geometry before writing variants
+ok 76 - delivery rejects failed proof, changed registration and changed prepared geometry before writing variants
+ ---
+ duration_ms: 15.02225
+ type: 'test'
+ ...
+# Subtest: an omitted normalization block keeps the accepted display levels exactly
+ok 77 - an omitted normalization block keeps the accepted display levels exactly
+ ---
+ duration_ms: 0.595792
+ type: 'test'
+ ...
+# Subtest: authored display levels are validated, ordered and closed
+ok 78 - authored display levels are validated, ordered and closed
+ ---
+ duration_ms: 0.375875
+ type: 'test'
+ ...
+# Subtest: a grid transfer needs a passing catalogue gate on the reference and an identical grid and WCS
+ok 79 - a grid transfer needs a passing catalogue gate on the reference and an identical grid and WCS
+ ---
+ duration_ms: 0.505
+ type: 'test'
+ ...
+# Subtest: recipes and candidates are validated at runtime
+ok 80 - recipes and candidates are validated at runtime
+ ---
+ duration_ms: 0.433959
+ type: 'test'
+ ...
+# Subtest: saved overlay state round-trips independently for each catalogue
+ok 81 - saved overlay state round-trips independently for each catalogue
+ ---
+ duration_ms: 0.935125
+ type: 'test'
+ ...
+# Subtest: bad records and missing placement properties are isolated from valid saved images
+ok 82 - bad records and missing placement properties are isolated from valid saved images
+ ---
+ duration_ms: 0.293833
+ type: 'test'
+ ...
+# Subtest: denied reads and quota-failed writes degrade safely
+ok 83 - denied reads and quota-failed writes degrade safely
+ ---
+ duration_ms: 0.725083
+ type: 'test'
+ ...
+# Subtest: corrected alignment replaces an untouched old default without erasing manual fits
+ok 84 - corrected alignment replaces an untouched old default without erasing manual fits
+ ---
+ duration_ms: 0.212041
+ type: 'test'
+ ...
+# Subtest: historical catalogue locations keep saved alignment after model folders move
+ok 85 - historical catalogue locations keep saved alignment after model folders move
+ ---
+ duration_ms: 0.090375
+ type: 'test'
+ ...
+# Subtest: TAN and ordinary SIN rays match independent Astropy WCS fixtures
+ok 86 - TAN and ordinary SIN rays match independent Astropy WCS fixtures
+ ---
+ duration_ms: 17.014167
+ type: 'test'
+ ...
+# Subtest: full image planes retain publisher angular footprints in each existing density frame
+ok 87 - full image planes retain publisher angular footprints in each existing density frame
+ ---
+ duration_ms: 2.154417
+ type: 'test'
+ ...
+# Subtest: the discovery catalogue has all 110 identities and preserves unknown extents
+ok 88 - the discovery catalogue has all 110 identities and preserves unknown extents
+ ---
+ duration_ms: 0.706959
+ type: 'test'
+ ...
+# Subtest: MAST ambiguous size units stay unknown and published file URLs distinguish one observation’s products
+ok 89 - MAST ambiguous size units stay unknown and published file URLs distinguish one observation’s products
+ ---
+ duration_ms: 1.316584
+ type: 'test'
+ ...
+# Subtest: a high-resolution partial field remains a detail candidate
+ok 90 - a high-resolution partial field remains a detail candidate
+ ---
+ duration_ms: 0.085167
+ type: 'test'
+ ...
+# Subtest: archive tables validate shape, failed responses and explicit overflow across JSON formats
+ok 91 - archive tables validate shape, failed responses and explicit overflow across JSON formats
+ ---
+ duration_ms: 0.182375
+ type: 'test'
+ ...
+# Subtest: unknown counts, duplicate file hits, and partial searches cannot produce a complete total
+ok 92 - unknown counts, duplicate file hits, and partial searches cannot produce a complete total
+ ---
+ duration_ms: 2.8265
+ type: 'test'
+ ...
+# Subtest: MAST discovery box wraps at RA zero; query scopes keep detailed partial images
+ok 93 - MAST discovery box wraps at RA zero; query scopes keep detailed partial images
+ ---
+ duration_ms: 0.175458
+ type: 'test'
+ ...
+# Subtest: largest-first uses apparent object extent and puts unknown sizes last
+ok 94 - largest-first uses apparent object extent and puts unknown sizes last
+ ---
+ duration_ms: 0.786958
+ type: 'test'
+ ...
+# Subtest: a sourced nebula extent can supersede its associated cluster, never the survey preview field
+ok 95 - a sourced nebula extent can supersede its associated cluster, never the survey preview field
+ ---
+ duration_ms: 0.156958
+ type: 'test'
+ ...
+# Subtest: canonical padded SIMBAD IDs are retained; duplicate/missing Messier identities fail
+ok 96 - canonical padded SIMBAD IDs are retained; duplicate/missing Messier identities fail
+ ---
+ duration_ms: 1.163708
+ type: 'test'
+ ...
+# Subtest: source metadata remains intact and missing abstracts stay missing
+ok 97 - source metadata remains intact and missing abstracts stay missing
+ ---
+ duration_ms: 0.45275
+ type: 'test'
+ ...
+# Subtest: object title priority is distinct from incidental bibliography membership and keyword topics
+ok 98 - object title priority is distinct from incidental bibliography membership and keyword topics
+ ---
+ duration_ms: 1.279958
+ type: 'test'
+ ...
+# Subtest: external links encode identifiers and never infer an available PDF
+ok 99 - external links encode identifiers and never infer an available PDF
+ ---
+ duration_ms: 0.198584
+ type: 'test'
+ ...
+# Subtest: loaded compressed pages must pass hash, byte count and object identity before display
+ok 100 - loaded compressed pages must pass hash, byte count and object identity before display
+ ---
+ duration_ms: 34.838875
+ type: 'test'
+ ...
+# Subtest: complete presentation pins real cutouts independently of object extents
+ok 101 - complete presentation pins real cutouts independently of object extents
+ ---
+ duration_ms: 3.988875
+ type: 'test'
+ ...
+# Subtest: missing pins, unsafe paths and duplicate objects fail validation
+ok 102 - missing pins, unsafe paths and duplicate objects fail validation
+ ---
+ duration_ms: 3.092334
+ type: 'test'
+ ...
+# Subtest: thumbnail decoder rejects changed pixels and incorrect dimensions
+ok 103 - thumbnail decoder rejects changed pixels and incorrect dimensions
+ ---
+ duration_ms: 6.445875
+ type: 'test'
+ ...
+# Subtest: every survey uses the object center, same north-up scale and rendered JPEG, without archive-product substitution
+ok 104 - every survey uses the object center, same north-up scale and rendered JPEG, without archive-product substitution
+ ---
+ duration_ms: 0.689583
+ type: 'test'
+ ...
+# Subtest: published compiler receipt requires a unique complete configured input set
+ok 105 - published compiler receipt requires a unique complete configured input set
+ ---
+ duration_ms: 18.325416
+ type: 'test'
+ ...
+# Subtest: changed current input bytes prevent displaying the old cloud
+ok 106 - changed current input bytes prevent displaying the old cloud
+ ---
+ duration_ms: 3.221583
+ type: 'test'
+ ...
+# Subtest: absent CLI publication is an empty workspace, not an implicit processing request
+ok 107 - absent CLI publication is an empty workspace, not an implicit processing request
+ ---
+ duration_ms: 0.227208
+ type: 'test'
+ ...
+# Subtest: saved clouds remain inspectable after producer code changes without rewriting its historical hash
+ok 108 - saved clouds remain inspectable after producer code changes without rewriting its historical hash
+ ---
+ duration_ms: 5.915666
+ type: 'test'
+ ...
+# Subtest: legacy and fully pinned depth receipts both restore without processing
+ok 109 - legacy and fully pinned depth receipts both restore without processing
+ ---
+ duration_ms: 5.282333
+ type: 'test'
+ ...
+# Subtest: photometric publication requires the exact model and evidence used for the saved volume
+ok 110 - photometric publication requires the exact model and evidence used for the saved volume
+ ---
+ duration_ms: 13.939417
+ type: 'test'
+ ...
+# Subtest: a current publication can replace a saved result only for the same controls, evidence and registration
+ok 111 - a current publication can replace a saved result only for the same controls, evidence and registration
+ ---
+ duration_ms: 6.349125
+ type: 'test'
+ ...
+# Subtest: publication cannot omit its configured depth recipe or declared evidence ledger
+ok 112 - publication cannot omit its configured depth recipe or declared evidence ledger
+ ---
+ duration_ms: 9.25575
+ type: 'test'
+ ...
+# Subtest: depth and evidence inputs cannot drift or change object identity
+ok 113 - depth and evidence inputs cannot drift or change object identity
+ ---
+ duration_ms: 7.441458
+ type: 'test'
+ ...
+# Subtest: current depth pins cannot relabel a historical bake with missing or different depth snapshots
+ok 114 - current depth pins cannot relabel a historical bake with missing or different depth snapshots
+ ---
+ duration_ms: 12.558875
+ type: 'test'
+ ...
+# Subtest: relocated producer closures restore without fetching current code or changing historical hashes
+ok 115 - relocated producer closures restore without fetching current code or changing historical hashes
+ ---
+ duration_ms: 138.608375
+ type: 'test'
+ ...
+# Subtest: combined evidence boundary rejects escaped paths, singular registrations and unbounded settings
+ok 116 - combined evidence boundary rejects escaped paths, singular registrations and unbounded settings
+ ---
+ duration_ms: 0.65025
+ type: 'test'
+ ...
+# Subtest: prepared evidence requires finite dimensions, source ownership and hashed local assets
+ok 117 - prepared evidence requires finite dimensions, source ownership and hashed local assets
+ ---
+ duration_ms: 0.399416
+ type: 'test'
+ ...
+# Subtest: detector requests and proposal receipts require bounded settings and exact source pins
+ok 118 - detector requests and proposal receipts require bounded settings and exact source pins
+ ---
+ duration_ms: 0.821292
+ type: 'test'
+ ...
+# Subtest: a clean lab offers originals until valid extracted previews exist
+ok 119 - a clean lab offers originals until valid extracted previews exist
+ ---
+ duration_ms: 16.229875
+ type: 'test'
+ ...
+# Subtest: legacy view links retain object and saved reconstruction state
+ok 120 - legacy view links retain object and saved reconstruction state
+ ---
+ duration_ms: 0.424709
+ type: 'test'
+ ...
+# Subtest: explicit paths take precedence and switching changes only the view
+ok 121 - explicit paths take precedence and switching changes only the view
+ ---
+ duration_ms: 0.1335
+ type: 'test'
+ ...
+# Subtest: all pages share object context while old catalogue bookmarks remain readable
+ok 122 - all pages share object context while old catalogue bookmarks remain readable
+ ---
+ duration_ms: 0.543542
+ type: 'test'
+ ...
+# Subtest: choosing another lens repaints the retained leaves without changing geometry identities
+ok 123 - choosing another lens repaints the retained leaves without changing geometry identities
+ ---
+ duration_ms: 1.196708
+ type: 'test'
+ ...
+# Subtest: only saved results naming the same model geometry and star layer may swap in place
+ok 124 - only saved results naming the same model geometry and star layer may swap in place
+ ---
+ duration_ms: 0.125041
+ type: 'test'
+ ...
+# Subtest: material toggles, lens swaps and density cutoffs share one texture-slot generation
+ok 125 - material toggles, lens swaps and density cutoffs share one texture-slot generation
+ ---
+ duration_ms: 0.828125
+ type: 'test'
+ ...
+# Subtest: replaced slots resynchronize star support with the displayed selection
+ok 126 - replaced slots resynchronize star support with the displayed selection
+ ---
+ duration_ms: 0.226
+ type: 'test'
+ ...
+# Subtest: the viewer routes every texture writer through the shared slots and resyncs stars on commit
+ok 127 - the viewer routes every texture writer through the shared slots and resyncs stars on commit
+ ---
+ duration_ms: 1.600542
+ type: 'test'
+ ...
+# Subtest: image layers reject changed source bindings, cropped grids, unsafe paths and duplicate identities
+ok 128 - image layers reject changed source bindings, cropped grids, unsafe paths and duplicate identities
+ ---
+ duration_ms: 1.132958
+ type: 'test'
+ ...
+# Subtest: prepared geometry is bound to the exact source, map and working image grid
+ok 129 - prepared geometry is bound to the exact source, map and working image grid
+ ---
+ duration_ms: 0.836959
+ type: 'test'
+ ...
+# Subtest: geometry boundary rejects invalid radii, support, scores and symmetry membership
+ok 130 - geometry boundary rejects invalid radii, support, scores and symmetry membership
+ ---
+ duration_ms: 0.237292
+ type: 'test'
+ ...
+# Subtest: optional geometry references use a relative path and immutable content hash
+ok 131 - optional geometry references use a relative path and immutable content hash
+ ---
+ duration_ms: 0.991917
+ type: 'test'
+ ...
+# Subtest: complete ellipse support decodes into two non-degenerate SVG arcs
+ok 132 - complete ellipse support decodes into two non-degenerate SVG arcs
+ ---
+ duration_ms: 0.182333
+ type: 'test'
+ ...
+# Subtest: alignment uses native image edges regardless of preview resolution
+ok 133 - alignment uses native image edges regardless of preview resolution
+ ---
+ duration_ms: 1.063292
+ type: 'test'
+ ...
+# Subtest: manual fit rotates around registered center and leaves original registration intact
+ok 134 - manual fit rotates around registered center and leaves original registration intact
+ ---
+ duration_ms: 0.19025
+ type: 'test'
+ ...
+# Subtest: invalid sky registration and unsafe source paths fail at JSON boundary
+ok 135 - invalid sky registration and unsafe source paths fail at JSON boundary
+ ---
+ duration_ms: 0.360083
+ type: 'test'
+ ...
+# Subtest: every new candidate has a source dossier and primary-paper context
+ok 136 - every new candidate has a source dossier and primary-paper context
+ ---
+ duration_ms: 9.681958
+ type: 'test'
+ ...
+# Subtest: curation preserves useful publisher-registered comparisons without upgrading their evidence
+ok 137 - curation preserves useful publisher-registered comparisons without upgrading their evidence
+ ---
+ duration_ms: 3.908458
+ type: 'test'
+ ...
+# Subtest: publisher-only images carry no invented stellar measurements
+ok 138 - publisher-only images carry no invented stellar measurements
+ ---
+ duration_ms: 0.6355
+ type: 'test'
+ ...
+# Subtest: wide optical intake accepts a ten-degree TAN footprint without relaxing registration
+ok 139 - wide optical intake accepts a ten-degree TAN footprint without relaxing registration
+ ---
+ duration_ms: 1.345792
+ type: 'test'
+ ...
+# Subtest: review maps reject changed native registration or source even at the same working size
+ok 140 - review maps reject changed native registration or source even at the same working size
+ ---
+ duration_ms: 1.095459
+ type: 'test'
+ ...
+# Subtest: review sprites must fit both their image bounds and prepared atlas support extent
+ok 141 - review sprites must fit both their image bounds and prepared atlas support extent
+ ---
+ duration_ms: 0.250833
+ type: 'test'
+ ...
+# Subtest: review identity is isolated by source and exact output; missing decisions remain unreviewed
+ok 142 - review identity is isolated by source and exact output; missing decisions remain unreviewed
+ ---
+ duration_ms: 1.254459
+ type: 'test'
+ ...
+# Subtest: wide-source registration retains explicit native discovery settings without changing other sources
+ok 143 - wide-source registration retains explicit native discovery settings without changing other sources
+ ---
+ duration_ms: 1.492542
+ type: 'test'
+ ...
+# Subtest: registration detector settings reject unbounded, noninteger and incompatible discovery
+ok 144 - registration detector settings reject unbounded, noninteger and incompatible discovery
+ ---
+ duration_ms: 1.221042
+ type: 'test'
+ ...
+# Subtest: composed sky band sources cache under their hash; publisher sources keep their downloaded name
+ok 145 - composed sky band sources cache under their hash; publisher sources keep their downloaded name
+ ---
+ duration_ms: 0.181042
+ type: 'test'
+ ...
+# Subtest: png recolors real-reference quads while preserving all geometry and every alpha byte
+ok 146 - png recolors real-reference quads while preserving all geometry and every alpha byte
+ ---
+ duration_ms: 26.337792
+ type: 'test'
+ ...
+# Subtest: webp recolors real-reference quads while preserving all geometry and every alpha byte
+ok 147 - webp recolors real-reference quads while preserving all geometry and every alpha byte
+ ---
+ duration_ms: 66.434625
+ type: 'test'
+ ...
+# Subtest: missing image or black chromaticity retains reference color/support, with explicit coverage accounting
+ok 148 - missing image or black chromaticity retains reference color/support, with explicit coverage accounting
+ ---
+ duration_ms: 22.3145
+ type: 'test'
+ ...
+# Subtest: image brightness cannot redefine opacity or reference support
+ok 149 - image brightness cannot redefine opacity or reference support
+ ---
+ duration_ms: 37.876917
+ type: 'test'
+ ...
+# Subtest: changed reference bytes fail before recoloring
+ok 150 - changed reference bytes fail before recoloring
+ ---
+ duration_ms: 4.564792
+ type: 'test'
+ ...
+# Subtest: saturation and registered detail change only RGB, including identical dark lanes on all slice axes
+ok 151 - saturation and registered detail change only RGB, including identical dark lanes on all slice axes
+ ---
+ duration_ms: 98.962375
+ type: 'test'
+ ...
+# Subtest: material brightness and gamma affect real midtone pixels while opacity stays exact
+ok 152 - material brightness and gamma affect real midtone pixels while opacity stays exact
+ ---
+ duration_ms: 85.819416
+ type: 'test'
+ ...
+# Subtest: whole-cloud tone also reaches uncovered and black-image neutral material
+ok 153 - whole-cloud tone also reaches uncovered and black-image neutral material
+ ---
+ duration_ms: 14.417
+ type: 'test'
+ ...
+# Subtest: prepared 3D component mixtures preserve their intensity and valid black material without changing alpha
+ok 154 - prepared 3D component mixtures preserve their intensity and valid black material without changing alpha
+ ---
+ duration_ms: 65.920958
+ type: 'test'
+ ...
+# Subtest: radial panel plots dark bins and replaces the measurement when the lens changes
+ok 155 - radial panel plots dark bins and replaces the measurement when the lens changes
+ ---
+ duration_ms: 379.77725
+ type: 'test'
+ ...
+# Subtest: finite emission and unknown saved methods cannot invoke density repaint
+ok 156 - finite emission and unknown saved methods cannot invoke density repaint
+ ---
+ duration_ms: 0.468875
+ type: 'test'
+ ...
+# Subtest: saved-result processing and resumed jobs guard the actual callbacks
+ok 157 - saved-result processing and resumed jobs guard the actual callbacks
+ ---
+ duration_ms: 0.291792
+ type: 'test'
+ ...
+# Subtest: Preview follows the selected image and its owning finite model, not the displayed result
+ok 158 - Preview follows the selected image and its owning finite model, not the displayed result
+ ---
+ duration_ms: 0.410791
+ type: 'test'
+ ...
+# Subtest: a linked or remembered result is ignored unless the current finite model baked it
+ok 159 - a linked or remembered result is ignored unless the current finite model baked it
+ ---
+ duration_ms: 0.125
+ type: 'test'
+ ...
+# Subtest: the controls use these rules for links, restored display and every Preview gate
+ok 160 - the controls use these rules for links, restored display and every Preview gate
+ ---
+ duration_ms: 0.642542
+ type: 'test'
+ ...
+# Subtest: a sampled publication cannot silently drop or relabel its qualified source and implementation pins
+ok 161 - a sampled publication cannot silently drop or relabel its qualified source and implementation pins
+ ---
+ duration_ms: 0.891417
+ type: 'test'
+ ...
+# Subtest: the shared FITS decoder is a pinned preparation owner, not an arbitrary tools path
+ok 162 - the shared FITS decoder is a pinned preparation owner, not an arbitrary tools path
+ ---
+ duration_ms: 0.129583
+ type: 'test'
+ ...
+# Subtest: relocated sampled owners and historical receipts remain readable without allowing unrelated package code
+ok 163 - relocated sampled owners and historical receipts remain readable without allowing unrelated package code
+ ---
+ duration_ms: 0.972708
+ type: 'test'
+ ...
+# Subtest: flat photo corners follow the actual cloud camera through compound yaw and pitch
+ok 164 - flat photo corners follow the actual cloud camera through compound yaw and pitch
+ ---
+ duration_ms: 1.893375
+ type: 'test'
+ ...
+# Subtest: triad uses source axes, registered screen orientation and the correct Earth-facing depth sign
+ok 165 - triad uses source axes, registered screen orientation and the correct Earth-facing depth sign
+ ---
+ duration_ms: 0.673916
+ type: 'test'
+ ...
+# Subtest: continuous drag displays bounded drafts and coalesces to latest while one job runs
+ok 166 - continuous drag displays bounded drafts and coalesces to latest while one job runs
+ ---
+ duration_ms: 0.994958
+ type: 'test'
+ ...
+# Subtest: resuming input cancels one obsolete final and rejects its late result
+ok 167 - resuming input cancels one obsolete final and rejects its late result
+ ---
+ duration_ms: 0.777709
+ type: 'test'
+ ...
+# Subtest: keyboard idle refines, cached final does not bake, and disconnect never cancels server work
+ok 168 - keyboard idle refines, cached final does not bake, and disconnect never cancels server work
+ ---
+ duration_ms: 0.263667
+ type: 'test'
+ ...
+# Subtest: failed processing stops automatic retries until a user retry or new edit
+ok 169 - failed processing stops automatic retries until a user retry or new edit
+ ---
+ duration_ms: 0.1325
+ type: 'test'
+ ...
+# Subtest: bulk shape edits preserve relative centers, angles, and size ratios
+ok 170 - bulk shape edits preserve relative centers, angles, and size ratios
+ ---
+ duration_ms: 0.716
+ type: 'test'
+ ...
+# Subtest: scope uses actual group membership and emission edits share a value
+ok 171 - scope uses actual group membership and emission edits share a value
+ ---
+ duration_ms: 0.174583
+ type: 'test'
+ ...
+# Subtest: removal strength defaults to 100 and survives reload independently for each original source
+ok 172 - removal strength defaults to 100 and survives reload independently for each original source
+ ---
+ duration_ms: 0.598167
+ type: 'test'
+ ...
+# Subtest: malformed records cannot suppress valid source preferences and unavailable storage keeps session values
+ok 173 - malformed records cannot suppress valid source preferences and unavailable storage keeps session values
+ ---
+ duration_ms: 0.101458
+ type: 'test'
+ ...
+# Subtest: image choice survives a late or replaced shell destination without resetting selection
+ok 174 - image choice survives a late or replaced shell destination without resetting selection
+ ---
+ duration_ms: 311.816458
+ type: 'test'
+ ...
+# Subtest: workspace portals keep navigation visibility, selected credit and accessible help in one retained scene
+ok 175 - workspace portals keep navigation visibility, selected credit and accessible help in one retained scene
+ ---
+ duration_ms: 465.726292
+ type: 'test'
+ ...
+# Subtest: floating tools toggle one panel above the buttons, close on Escape and hide without a source image
+ok 176 - floating tools toggle one panel above the buttons, close on Escape and hide without a source image
+ ---
+ duration_ms: 534.676208
+ type: 'test'
+ ...
+# Subtest: all workspaces show the same ordered sections regardless of available capabilities
+ok 177 - all workspaces show the same ordered sections regardless of available capabilities
+ ---
+ duration_ms: 9.485542
+ type: 'test'
+ ...
+# Subtest: configured tools stay enabled and unavailable velocity keeps its explanatory reason
+ok 178 - configured tools stay enabled and unavailable velocity keeps its explanatory reason
+ ---
+ duration_ms: 3.65075
+ type: 'test'
+ ...
+# Subtest: the curated lab retains the selected LMC images, SMC comparisons and verified density banks
+not ok 179 - the curated lab retains the selected LMC images, SMC comparisons and verified density banks
+ ---
+ duration_ms: 11.37475
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/resources/model-catalogue.test.mjs:32:1'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, open '/private/tmp/cssearth-pr390/labs/nebula/models/lmc/candidates/prepared/vista-infrared.webp'"
+ code: 'ENOENT'
+ stack: |-
+ async open (node:internal/fs/promises:639:25)
+ async readFile (node:internal/fs/promises:1252:14)
+ async TestContext. (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/resources/model-catalogue.test.mjs:54:19)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3)
+ ...
+# Subtest: historical model locations resolve without changing unrelated paths or caller JSON bytes
+ok 180 - historical model locations resolve without changing unrelated paths or caller JSON bytes
+ ---
+ duration_ms: 0.474208
+ type: 'test'
+ ...
+# Subtest: moved native volume recipe keeps sibling grid and provenance bytes under their original pins
+ok 181 - moved native volume recipe keeps sibling grid and provenance bytes under their original pins
+ ---
+ duration_ms: 6.68475
+ type: 'test'
+ ...
+# Subtest: Apply survives a discarded HTTP response and fresh observers; persisted request identity starts exactly one real child
+ok 182 - Apply survives a discarded HTTP response and fresh observers; persisted request identity starts exactly one real child
+ ---
+ duration_ms: 765.73625
+ type: 'test'
+ ...
+# Subtest: GET of a completed job with unavailable artifacts returns a persisted terminal state without launching work
+ok 183 - GET of a completed job with unavailable artifacts returns a persisted terminal state without launching work
+ ---
+ duration_ms: 7.197916
+ type: 'test'
+ ...
+# Subtest: only explicit Cancel stops the child; cancellation persists and neither polling nor repeated start retries it
+ok 184 - only explicit Cancel stops the child; cancellation persists and neither polling nor repeated start retries it
+ ---
+ duration_ms: 53.597875
+ type: 'test'
+ ...
+# Subtest: unfinished jobs become honestly interrupted on restart and shutdown, without automatic work
+ok 185 - unfinished jobs become honestly interrupted on restart and shutdown, without automatic work
+ ---
+ duration_ms: 53.570333
+ type: 'test'
+ ...
+# Subtest: legacy manual job records cannot block the automatic NOX job namespace
+ok 186 - legacy manual job records cannot block the automatic NOX job namespace
+ ---
+ duration_ms: 7.716667
+ type: 'test'
+ ...
+# Subtest: opt-in preview history crosses 512 real jobs while retaining active, recent and preferred receipts and all artifacts
+ok 187 - opt-in preview history crosses 512 real jobs while retaining active, recent and preferred receipts and all artifacts
+ ---
+ duration_ms: 1814.395958
+ type: 'test'
+ ...
+# Subtest: default processing history remains non-archiving at its existing limit
+ok 188 - default processing history remains non-archiving at its existing limit
+ ---
+ duration_ms: 90.319958
+ type: 'test'
+ ...
+# Subtest: durable jobs accept validated operation scopes without star-removal request fields
+ok 189 - durable jobs accept validated operation scopes without star-removal request fields
+ ---
+ duration_ms: 16.256167
+ type: 'test'
+ ...
+# Subtest: constructing plugin configuration does not initialize or recover job storage
+ok 190 - constructing plugin configuration does not initialize or recover job storage
+ ---
+ duration_ms: 63.107917
+ type: 'test'
+ ...
+# Subtest: shape preview request validates its actual image grid and explicit settings
+ok 191 - shape preview request validates its actual image grid and explicit settings
+ ---
+ duration_ms: 1.196166
+ type: 'test'
+ ...
+# Subtest: completed shape result requires both material pins, finite registration and exact settings
+ok 192 - completed shape result requires both material pins, finite registration and exact settings
+ ---
+ duration_ms: 1.762167
+ type: 'test'
+ ...
+# Subtest: the catalogue restores the newest completed placement, independent of hash ordering
+ok 193 - the catalogue restores the newest completed placement, independent of hash ordering
+ ---
+ duration_ms: 123.040875
+ type: 'test'
+ ...
+# Subtest: reconstruction requires an explicit operation, a completed removal identity and finite alignment
+ok 194 - reconstruction requires an explicit operation, a completed removal identity and finite alignment
+ ---
+ duration_ms: 0.825208
+ type: 'test'
+ ...
+# Subtest: reconstruction jobs survive observers independently of the star-removal job namespace
+ok 195 - reconstruction jobs survive observers independently of the star-removal job namespace
+ ---
+ duration_ms: 31.940167
+ type: 'test'
+ ...
+# Subtest: lenses are discovered from the newest fitted model bundle, never from fixed identities
+ok 196 - lenses are discovered from the newest fitted model bundle, never from fixed identities
+ ---
+ duration_ms: 132.784083
+ type: 'test'
+ ...
+# Subtest: a bundle whose lens belongs to another model or lacks its model result is not selectable
+ok 197 - a bundle whose lens belongs to another model or lacks its model result is not selectable
+ ---
+ duration_ms: 52.157833
+ type: 'test'
+ ...
+# Subtest: a saved lens cannot claim a shared model its pinned provenance does not name
+ok 198 - a saved lens cannot claim a shared model its pinned provenance does not name
+ ---
+ duration_ms: 20.308917
+ type: 'test'
+ ...
+# Subtest: the catalogue keeps every candidate, marks images without a lens unavailable and reports rejected newer models
+ok 199 - the catalogue keeps every candidate, marks images without a lens unavailable and reports rejected newer models
+ ---
+ duration_ms: 70.313958
+ type: 'test'
+ ...
+# Subtest: one prepared star layer of a model is referenced by every lens, and stale or foreign stars reject the bundle
+ok 200 - one prepared star layer of a model is referenced by every lens, and stale or foreign stars reject the bundle
+ ---
+ duration_ms: 44.083042
+ type: 'test'
+ ...
+# Subtest: cache identity includes transitive numerical owners and leaves no build output
+ok 201 - cache identity includes transitive numerical owners and leaves no build output
+ ---
+ duration_ms: 69.9005
+ type: 'test'
+ ...
+# Subtest: relocated preparation entry points pin their live package owners
+ok 202 - relocated preparation entry points pin their live package owners
+ ---
+ duration_ms: 106.955292
+ type: 'test'
+ ...
+# Subtest: sampled supplementary owner allowlist points at existing implementation files
+ok 203 - sampled supplementary owner allowlist points at existing implementation files
+ ---
+ duration_ms: 1.332292
+ type: 'test'
+ ...
+# Subtest: the delivery factor is divided out: an analytic render exactly that much brighter reads as agreement
+ok 204 - the delivery factor is divided out: an analytic render exactly that much brighter reads as agreement
+ ---
+ duration_ms: 1.143833
+ type: 'test'
+ ...
+# Subtest: the tolerance is the stated one: 5 levels off stays clear, 8 levels off is coloured with its sign
+ok 205 - the tolerance is the stated one: 5 levels off stays clear, 8 levels off is coloured with its sign
+ ---
+ duration_ms: 3.828625
+ type: 'test'
+ ...
+# Subtest: pixels outside the footprint stay transparent however far off they are
+ok 206 - pixels outside the footprint stay transparent however far off they are
+ ---
+ duration_ms: 0.265625
+ type: 'test'
+ ...
+# Subtest: the route answers a transparent PNG for a lens that matches once delivery is divided out, and its legend JSON
+ok 207 - the route answers a transparent PNG for a lens that matches once delivery is divided out, and its legend JSON
+ ---
+ duration_ms: 124.52475
+ type: 'test'
+ ...
+# Subtest: the route refuses writes, bad identities and non-lens results
+ok 208 - the route refuses writes, bad identities and non-lens results
+ ---
+ duration_ms: 18.132583
+ type: 'test'
+ ...
+# Subtest: the map is written in the lens image frame, the frame the original-image overlay covers
+ok 209 - the map is written in the lens image frame, the frame the original-image overlay covers
+ ---
+ duration_ms: 15.186333
+ type: 'test'
+ ...
+# Subtest: the Horálek lenses tell the truth: the bar reads too dark, and tone fitting removes most of the red
+ok 210 - the Horálek lenses tell the truth: the bar reads too dark, and tone fitting removes most of the red # SKIP The Horálek lenses are not baked in this checkout.
+ ---
+ duration_ms: 1.063458
+ type: 'test'
+ ...
+# Subtest: a projection that matches its own image reports unit ratios, a zero delta and an identity transfer
+ok 211 - a projection that matches its own image reports unit ratios, a zero delta and an identity transfer
+ ---
+ duration_ms: 4.783208
+ type: 'test'
+ ...
+# Subtest: a brighter projection lifts the ratios, the delta and the transfer above the diagonal by the same factor
+ok 212 - a brighter projection lifts the ratios, the delta and the transfer above the diagonal by the same factor
+ ---
+ duration_ms: 6.153125
+ type: 'test'
+ ...
+# Subtest: only the overlay footprint is measured, and dropping the mask would change every number
+ok 213 - only the overlay footprint is measured, and dropping the mask would change every number
+ ---
+ duration_ms: 8.374542
+ type: 'test'
+ ...
+# Subtest: the photograph’s sky pedestal is removed from the source and never from the render
+ok 214 - the photograph’s sky pedestal is removed from the source and never from the render
+ ---
+ duration_ms: 3.542625
+ type: 'test'
+ ...
+# Subtest: the lens raster is placed through its own tangent bounds, never stretched onto the projection grid
+ok 215 - the lens raster is placed through its own tangent bounds, never stretched onto the projection grid
+ ---
+ duration_ms: 2.825542
+ type: 'test'
+ ...
+# Subtest: one saved lens is measured from its own pinned rasters, and only from them
+ok 216 - one saved lens is measured from its own pinned rasters, and only from them
+ ---
+ duration_ms: 79.403625
+ type: 'test'
+ ...
+# Subtest: a lens raster that no longer matches its manifest pin is refused, and a non-lens result is refused
+ok 217 - a lens raster that no longer matches its manifest pin is refused, and a non-lens result is refused
+ ---
+ duration_ms: 38.644792
+ type: 'test'
+ ...
+# Subtest: a lens registered on half the model field covers half the footprint
+ok 218 - a lens registered on half the model field covers half the footprint
+ ---
+ duration_ms: 15.810584
+ type: 'test'
+ ...
+# Subtest: the route’s own accepted LMC lenses are measured, when they are present in this checkout
+ok 219 - the route’s own accepted LMC lenses are measured, when they are present in this checkout # SKIP The accepted Horálek lens is not baked in this checkout.
+ ---
+ duration_ms: 1.129791
+ type: 'test'
+ ...
+# Subtest: a render that matches its source at every radius reports agreement, not just an overall match
+ok 220 - a render that matches its source at every radius reports agreement, not just an overall match
+ ---
+ duration_ms: 4.621959
+ type: 'test'
+ ...
+# Subtest: a centre <-> annulus brightness swap conserves the histogram exactly, and levels cannot see it, but the radial profile flags it
+ok 221 - a centre <-> annulus brightness swap conserves the histogram exactly, and levels cannot see it, but the radial profile flags it
+ ---
+ duration_ms: 7.977167
+ type: 'test'
+ ...
+# Subtest: a radially compressed render is caught by the half-light radius even though no single bin is wildly wrong
+ok 222 - a radially compressed render is caught by the half-light radius even though no single bin is wildly wrong
+ ---
+ duration_ms: 3.4635
+ type: 'test'
+ ...
+# Subtest: a missing bright core remains the worst radial defect even when its render is exactly zero
+ok 223 - a missing bright core remains the worst radial defect even when its render is exactly zero
+ ---
+ duration_ms: 2.868833
+ type: 'test'
+ ...
+# Subtest: the footprint mask is respected, and empty or degenerate bins report null rather than NaN
+ok 224 - the footprint mask is respected, and empty or degenerate bins report null rather than NaN
+ ---
+ duration_ms: 1.039542
+ type: 'test'
+ ...
+# Subtest: the route answers the radial profile JSON for a saved lens whose render matches its own source
+ok 225 - the route answers the radial profile JSON for a saved lens whose render matches its own source
+ ---
+ duration_ms: 67.223459
+ type: 'test'
+ ...
+# Subtest: the route refuses writes, bad identities and non-lens results
+ok 226 - the route refuses writes, bad identities and non-lens results
+ ---
+ duration_ms: 10.3725
+ type: 'test'
+ ...
+# Subtest: automatic requests accept only image identity and overview, preview or apply
+ok 227 - automatic requests accept only image identity and overview, preview or apply
+ ---
+ duration_ms: 1.1955
+ type: 'test'
+ ...
+# Subtest: original overview runs no inference; native previews deduplicate, report progress, cache and preserve approved input
+ok 228 - original overview runs no inference; native previews deduplicate, report progress, cache and preserve approved input
+ ---
+ duration_ms: 178.267917
+ type: 'test'
+ ...
+# Subtest: Apply needs no calibration and its verified cache restores after moving the checkout root
+ok 229 - Apply needs no calibration and its verified cache restores after moving the checkout root
+ ---
+ duration_ms: 152.714292
+ type: 'test'
+ ...
+# Subtest: fresh NOX Apply works without legacy detections, separated files or variant catalogue
+ok 230 - fresh NOX Apply works without legacy detections, separated files or variant catalogue
+ ---
+ duration_ms: 78.1225
+ type: 'test'
+ ...
+# Subtest: an imported candidate needs no trial selection, alignment gate, recipe or existing star layers
+ok 231 - an imported candidate needs no trial selection, alignment gate, recipe or existing star layers
+ ---
+ duration_ms: 76.00975
+ type: 'test'
+ ...
+# Subtest: a candidate outside the saved selection processes without changing earlier result identities
+ok 232 - a candidate outside the saved selection processes without changing earlier result identities
+ ---
+ duration_ms: 147.720542
+ type: 'test'
+ ...
+# Subtest: relocating catalogue metadata preserves saved native output but changed native inputs still fail
+ok 233 - relocating catalogue metadata preserves saved native output but changed native inputs still fail
+ ---
+ duration_ms: 160.501917
+ type: 'test'
+ ...
+# Subtest: 16-bit imported images get a separate full-size RGB8 input while their original is preserved
+ok 234 - 16-bit imported images get a separate full-size RGB8 input while their original is preserved
+ ---
+ duration_ms: 111.35725
+ type: 'test'
+ ...
+# Subtest: output pins, native crop bounds and exact accounting are required before atomic publication
+ok 235 - output pins, native crop bounds and exact accounting are required before atomic publication
+ ---
+ duration_ms: 118.526208
+ type: 'test'
+ ...
+# Subtest: cancelling one observer keeps shared inference; cancelling the final observer removes incomplete output
+ok 236 - cancelling one observer keeps shared inference; cancelling the final observer removes incomplete output
+ ---
+ duration_ms: 58.460875
+ type: 'test'
+ ...
+# Subtest: native discovery restores verified completed images without launching removal and rejects changed model/source outputs
+ok 237 - native discovery restores verified completed images without launching removal and rejects changed model/source outputs
+ ---
+ duration_ms: 114.057375
+ type: 'test'
+ ...
+# Subtest: a configured preserve treatment keeps every native pixel, runs no inference and refuses a tampered identity
+ok 238 - a configured preserve treatment keeps every native pixel, runs no inference and refuses a tampered identity
+ ---
+ duration_ms: 88.6235
+ type: 'test'
+ ...
+# Subtest: tone formula applies ordered levels, gamma, gain; photograph alpha and density RGB stay unchanged
+ok 239 - tone formula applies ordered levels, gamma, gain; photograph alpha and density RGB stay unchanged
+ ---
+ duration_ms: 1.097375
+ type: 'test'
+ ...
+# Subtest: tone requests reject arbitrary paths, unrecognized/missing fields and non-finite or invalid levels
+ok 240 - tone requests reject arbitrary paths, unrecognized/missing fields and non-finite or invalid levels
+ ---
+ duration_ms: 1.190375
+ type: 'test'
+ ...
+# Subtest: removal requests are bounded and image-only; endpoint interpolation precedes tone and preserves alpha
+ok 241 - removal requests are bounded and image-only; endpoint interpolation precedes tone and preserves alpha
+ ---
+ duration_ms: 0.345
+ type: 'test'
+ ...
+# Subtest: saved reconstructions retain inherited Alignment image and density tone targets
+ok 242 - saved reconstructions retain inherited Alignment image and density tone targets
+ ---
+ duration_ms: 56.255084
+ type: 'test'
+ ...
+# Subtest: local preparation writes verifiable pixels, deduplicates concurrent cache work and returns original neutral resources
+ok 243 - local preparation writes verifiable pixels, deduplicates concurrent cache work and returns original neutral resources
+ ---
+ duration_ms: 71.742834
+ type: 'test'
+ ...
+# Subtest: image tone targets the selected prepared layer and rejects a mismatched original
+ok 244 - image tone targets the selected prepared layer and rejects a mismatched original
+ ---
+ duration_ms: 21.006584
+ type: 'test'
+ ...
+# Subtest: strength uses the selected full-size grid, preserves endpoints, scales residual before tone and caches original pairing
+ok 245 - strength uses the selected full-size grid, preserves endpoints, scales residual before tone and caches original pairing
+ ---
+ duration_ms: 69.885208
+ type: 'test'
+ ...
+# Subtest: actual prepared candidate matrices preserve Alignment CSS axes, pivot, all rotations and observer depth
+ok 246 - actual prepared candidate matrices preserve Alignment CSS axes, pivot, all rotations and observer depth
+ ---
+ duration_ms: 5.560917
+ type: 'test'
+ ...
+# Subtest: saved Alignment scale and orientation are preserved instead of stripping its fit
+ok 247 - saved Alignment scale and orientation are preserved instead of stripping its fit
+ ---
+ duration_ms: 1.71925
+ type: 'test'
+ ...
+# Subtest: tiny offline bake uses shared density support and writes pinned XYZ resources and cloud inspection
+not ok 248 - tiny offline bake uses shared density support and writes pinned XYZ resources and cloud inspection
+ ---
+ duration_ms: 1075.678208
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workers/density-reconstruction.test.mjs:2753:1'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, open '/private/tmp/cssearth-pr390/labs/nebula/models/lmc/full-density/prepared/slices/x/00.webp'"
+ code: 'ENOENT'
+ stack: |-
+ async open (node:internal/fs/promises:639:25)
+ async readFile (node:internal/fs/promises:1252:14)
+ async recolorCloudSlices (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workers/density-reconstruction.test.mjs:725:31)
+ async prepareReconstruction (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workers/density-reconstruction.test.mjs:2547:19)
+ async TestContext. (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workers/density-reconstruction.test.mjs:2793:20)
+ async Test.run (node:internal/test_runner/test:1054:7)
+ async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
+ ...
+# Subtest: worker completion must validate and exit successfully
+ok 249 - worker completion must validate and exit successfully
+ ---
+ duration_ms: 304.872875
+ type: 'test'
+ ...
+# Subtest: explicit cancellation terminates the actual worker before resolving
+ok 250 - explicit cancellation terminates the actual worker before resolving
+ ---
+ duration_ms: 71.494584
+ type: 'test'
+ ...
+# Subtest: compiler frame centers absolute west/north/away coordinates without changing their span
+ok 251 - compiler frame centers absolute west/north/away coordinates without changing their span
+ ---
+ duration_ms: 1.179667
+ type: 'test'
+ ...
+# Subtest: prepared compiler transport reflects source depth exactly once without changing image bytes or UVs
+ok 252 - prepared compiler transport reflects source depth exactly once without changing image bytes or UVs
+ ---
+ duration_ms: 1.116125
+ type: 'test'
+ ...
+# Subtest: default compiler bake refuses projected-image-only lenses before creating output
+ok 253 - default compiler bake refuses projected-image-only lenses before creating output
+ ---
+ duration_ms: 0.553875
+ type: 'test'
+ ...
+# Subtest: the actual CSS compiler backend rejects an oversized explicit plan before sampling or writing
+ok 254 - the actual CSS compiler backend rejects an oversized explicit plan before sampling or writing
+ ---
+ duration_ms: 0.386666
+ type: 'test'
+ ...
+# Subtest: thin supported features receive finer equally spaced banks without an unbounded slice count
+ok 255 - thin supported features receive finer equally spaced banks without an unbounded slice count
+ ---
+ duration_ms: 0.196167
+ type: 'test'
+ ...
+# Subtest: compiler alpha handoff checks decoded bytes, including transparent texels
+ok 256 - compiler alpha handoff checks decoded bytes, including transparent texels
+ ---
+ duration_ms: 22.195625
+ type: 'test'
+ ...
+# Subtest: standard RGB lenses still require exact neutral alpha when no component pin exists
+ok 257 - standard RGB lenses still require exact neutral alpha when no component pin exists
+ ---
+ duration_ms: 0.9905
+ type: 'test'
+ ...
+# Subtest: a stellar-only result retains the explicitly pinned cloud identity and rejects unrelated banks
+ok 258 - a stellar-only result retains the explicitly pinned cloud identity and rejects unrelated banks
+ ---
+ duration_ms: 0.229958
+ type: 'test'
+ ...
+# Subtest: sampled spectral alpha must equal its explicit pin, not merely be different from neutral
+ok 259 - sampled spectral alpha must equal its explicit pin, not merely be different from neutral
+ ---
+ duration_ms: 1.47
+ type: 'test'
+ ...
+# Subtest: spectral alpha never relaxes result, frame or field identity
+ok 260 - spectral alpha never relaxes result, frame or field identity
+ ---
+ duration_ms: 0.25075
+ type: 'test'
+ ...
+# Subtest: spectral alpha never relaxes shared slab count, position, dimensions or style
+ok 261 - spectral alpha never relaxes shared slab count, position, dimensions or style
+ ---
+ duration_ms: 0.204625
+ type: 'test'
+ ...
+# Subtest: apparent V magnitude ranks bright stars first, with measured color and neutral unknown color
+ok 262 - apparent V magnitude ranks bright stars first, with measured color and neutral unknown color
+ ---
+ duration_ms: 4.121458
+ type: 'test'
+ ...
+# Subtest: disk area and opacity jointly preserve V light ratios across colors with no faint floor
+ok 263 - disk area and opacity jointly preserve V light ratios across colors with no faint floor
+ ---
+ duration_ms: 1.474292
+ type: 'test'
+ ...
+# Subtest: TAN astrometry matches a known off-axis projection and rejects the linear RA approximation
+ok 264 - TAN astrometry matches a known off-axis projection and rejects the linear RA approximation
+ ---
+ duration_ms: 0.917292
+ type: 'test'
+ ...
+# Subtest: dust support never erases a measured star; all image lenses preserve the optical overlay
+ok 265 - dust support never erases a measured star; all image lenses preserve the optical overlay
+ ---
+ duration_ms: 1.048292
+ type: 'test'
+ ...
+# Subtest: malformed catalogue fields, epoch, duplicate identities and invalid limits fail before rendering
+ok 266 - malformed catalogue fields, epoch, duplicate identities and invalid limits fail before rendering
+ ---
+ duration_ms: 1.298958
+ type: 'test'
+ ...
+# Subtest: recipe defaults reach unsaved controls while explicit user choices win
+ok 267 - recipe defaults reach unsaved controls while explicit user choices win
+ ---
+ duration_ms: 1.037916
+ type: 'test'
+ ...
+# Subtest: photometric recipes cannot configure the hidden depth control away from their pinned prior
+ok 268 - photometric recipes cannot configure the hidden depth control away from their pinned prior
+ ---
+ duration_ms: 0.287791
+ type: 'test'
+ ...
+# Subtest: named source weights follow source IDs and explicit diagnostic weights override
+ok 269 - named source weights follow source IDs and explicit diagnostic weights override
+ ---
+ duration_ms: 0.311
+ type: 'test'
+ ...
+# Subtest: invalid authored controls, weights and source IDs are rejected
+ok 270 - invalid authored controls, weights and source IDs are rejected
+ ---
+ duration_ms: 0.367541
+ type: 'test'
+ ...
+# Subtest: the saved emission window is explicit, validated and unavailable for sampled volumes
+ok 271 - the saved emission window is explicit, validated and unavailable for sampled volumes
+ ---
+ duration_ms: 0.474292
+ type: 'test'
+ ...
+# Subtest: known quadratic surface conditions center, local slope and thickness with declared evidence attribution
+ok 272 - known quadratic surface conditions center, local slope and thickness with declared evidence attribution
+ ---
+ duration_ms: 1.326083
+ type: 'test'
+ ...
+# Subtest: scoped features match their centers and join the background continuously without duplicated surfaces
+ok 273 - scoped features match their centers and join the background continuously without duplicated surfaces
+ ---
+ duration_ms: 1.702625
+ type: 'test'
+ ...
+# Subtest: a requested minimum thickness cannot extrude sub-resolution details into a global rod floor
+ok 274 - a requested minimum thickness cannot extrude sub-resolution details into a global rod floor
+ ---
+ duration_ms: 2.52475
+ type: 'test'
+ ...
+# Subtest: conditioning preserves numerical z-integrated projected light at multiple rays
+ok 275 - conditioning preserves numerical z-integrated projected light at multiple rays
+ ---
+ duration_ms: 11.767792
+ type: 'test'
+ ...
+# Subtest: physical ledger rejects wrong subjects, missing evidence/methods and broken source attribution
+ok 276 - physical ledger rejects wrong subjects, missing evidence/methods and broken source attribution
+ ---
+ duration_ms: 0.543333
+ type: 'test'
+ ...
+# Subtest: invalid depth frames, surfaces, pin paths and conditioning scales are rejected
+ok 277 - invalid depth frames, surfaces, pin paths and conditioning scales are rejected
+ ---
+ duration_ms: 0.312
+ type: 'test'
+ ...
+# Subtest: source-owned depth inputs load with a content pin and reject changes or escaping symlinks
+ok 278 - source-owned depth inputs load with a content pin and reject changes or escaping symlinks
+ ---
+ duration_ms: 16.531417
+ type: 'test'
+ ...
+# Subtest: fit keeps its legacy default path and the conditioned field integrates to its published projection
+ok 279 - fit keeps its legacy default path and the conditioned field integrates to its published projection
+ ---
+ duration_ms: 22.215125
+ type: 'test'
+ ...
+# Subtest: a fitted optical window has the same clipped projection and numerical XYZ integral after serialization
+ok 280 - a fitted optical window has the same clipped projection and numerical XYZ integral after serialization
+ ---
+ duration_ms: 41.0095
+ type: 'test'
+ ...
+# Subtest: finite smooth neutral field retains XYZ support and its actual z integral matches the analytic projection
+ok 281 - finite smooth neutral field retains XYZ support and its actual z integral matches the analytic projection
+ ---
+ duration_ms: 6.108375
+ type: 'test'
+ ...
+# Subtest: positive multiscale fit improves an independent irregular target and retains all unassigned and excess signal
+ok 282 - positive multiscale fit improves an independent irregular target and retains all unassigned and excess signal
+ ---
+ duration_ms: 110.91475
+ type: 'test'
+ ...
+# Subtest: depth control changes only conditional depth and thickness while preserving integrated projection and deterministic shared geometry
+ok 283 - depth control changes only conditional depth and thickness while preserving integrated projection and deterministic shared geometry
+ ---
+ duration_ms: 146.70375
+ type: 'test'
+ ...
+# Subtest: unconstrained emission stays diffuse around zero depth without a footprint-sized shell and retains its integrated light
+ok 284 - unconstrained emission stays diffuse around zero depth without a footprint-sized shell and retains its integrated light
+ ---
+ duration_ms: 81.216792
+ type: 'test'
+ ...
+# Subtest: unobserved values cannot fit components and blank targets never become a slab or stellar plane
+ok 285 - unobserved values cannot fit components and blank targets never become a slab or stellar plane
+ ---
+ duration_ms: 36.125958
+ type: 'test'
+ ...
+# Subtest: pinned image layers use native rotation, scale, translation and sky offset for every working resolution
+ok 286 - pinned image layers use native rotation, scale, translation and sky offset for every working resolution
+ ---
+ duration_ms: 32.218416
+ type: 'test'
+ ...
+# Subtest: target retains missing coverage separately from observed zero and maps complete footprint bounds without mutating inputs
+ok 287 - target retains missing coverage separately from observed zero and maps complete footprint bounds without mutating inputs
+ ---
+ duration_ms: 1.088208
+ type: 'test'
+ ...
+# Subtest: excluded sources supply neither emission nor coverage and unavailable RGB cannot influence a live source target
+ok 288 - excluded sources supply neither emission nor coverage and unavailable RGB cannot influence a live source target
+ ---
+ duration_ms: 0.616375
+ type: 'test'
+ ...
+# Subtest: delivery composites a supplied result with no historical caches and retains geometry, alpha and stars
+ok 289 - delivery composites a supplied result with no historical caches and retains geometry, alpha and stars
+ ---
+ duration_ms: 2700.842458
+ type: 'test'
+ ...
+# Subtest: composite source readiness distinguishes missing native input from changed evidence and restores through the source owner
+ok 290 - composite source readiness distinguishes missing native input from changed evidence and restores through the source owner
+ ---
+ duration_ms: 224.656625
+ type: 'test'
+ ...
+# Subtest: native rotated and scaled overlap recovers channel gains and applies them across the wide footprint
+ok 291 - native rotated and scaled overlap recovers channel gains and applies them across the wide footprint
+ ---
+ duration_ms: 28.0525
+ type: 'test'
+ ...
+# Subtest: clipped and star-dominated samples are excluded and remaining diffuse outliers cannot set the fit
+ok 292 - clipped and star-dominated samples are excluded and remaining diffuse outliers cannot set the fit
+ ---
+ duration_ms: 14.541375
+ type: 'test'
+ ...
+# Subtest: feathering uses arcseconds after rotation and preserves interior detail and original sampling
+ok 293 - feathering uses arcseconds after rotation and preserves interior detail and original sampling
+ ---
+ duration_ms: 5.545916
+ type: 'test'
+ ...
+# Subtest: source coverage, interior holes and observed black remain separate without extrapolation
+ok 294 - source coverage, interior holes and observed black remain separate without extrapolation
+ ---
+ duration_ms: 2.537417
+ type: 'test'
+ ...
+# Subtest: fit limits bound attempted display corrections and sparse or constant overlap never invents calibration
+ok 295 - fit limits bound attempted display corrections and sparse or constant overlap never invents calibration
+ ---
+ duration_ms: 23.994416
+ type: 'test'
+ ...
+# Subtest: held-out disagreement rejects a misleading fit instead of applying invented calibration
+ok 296 - held-out disagreement rejects a misleading fit instead of applying invented calibration
+ ---
+ duration_ms: 0.792958
+ type: 'test'
+ ...
+# Subtest: default overlap correction preserves exact wide outskirts and blends continuously across a tilted footprint
+ok 297 - default overlap correction preserves exact wide outskirts and blends continuously across a tilted footprint
+ ---
+ duration_ms: 32.169875
+ type: 'test'
+ ...
+# Subtest: detail fusion preserves different broad backgrounds and colors without a rectangular reference patch
+ok 298 - detail fusion preserves different broad backgrounds and colors without a rectangular reference patch
+ ---
+ duration_ms: 0.344958
+ type: 'test'
+ ...
+# Subtest: Gaussian-separated fine ridges replace wide detail while preserving its broad illumination and chromaticity
+ok 299 - Gaussian-separated fine ridges replace wide detail while preserving its broad illumination and chromaticity
+ ---
+ duration_ms: 0.47675
+ type: 'test'
+ ...
+# Subtest: detail fusion requires prepared low-pass inputs, bounds extreme detail, and keeps black distinct from missing coverage
+ok 300 - detail fusion requires prepared low-pass inputs, bounds extreme detail, and keeps black distinct from missing coverage
+ ---
+ duration_ms: 11.109625
+ type: 'test'
+ ...
+# Subtest: photometric evidence validation rejects cross-subject or repinned source snapshots
+ok 301 - photometric evidence validation rejects cross-subject or repinned source snapshots
+ ---
+ duration_ms: 3.521375
+ type: 'test'
+ ...
+# Subtest: two-scale fit retains smooth light and finite residuals, including after JSON replay
+ok 302 - two-scale fit retains smooth light and finite residuals, including after JSON replay
+ ---
+ duration_ms: 50.331875
+ type: 'test'
+ ...
+# Subtest: authored residual budget bounds the fit and omitted budgets preserve historical defaults
+ok 303 - authored residual budget bounds the fit and omitted budgets preserve historical defaults
+ ---
+ duration_ms: 14.547666
+ type: 'test'
+ ...
+# Subtest: validated envelope chroma bounds the exact round2 green-channel roundoff without accepting nonfinite colors
+ok 304 - validated envelope chroma bounds the exact round2 green-channel roundoff without accepting nonfinite colors
+ ---
+ duration_ms: 1.478375
+ type: 'test'
+ ...
+# Subtest: compiler restores missing derived layers but refuses modified evidence
+ok 305 - compiler restores missing derived layers but refuses modified evidence
+ ---
+ duration_ms: 17.915458
+ type: 'test'
+ ...
+# Subtest: retained material prepares RGB through the existing slab/compiler path with exact geometry and alpha
+ok 306 - retained material prepares RGB through the existing slab/compiler path with exact geometry and alpha
+ ---
+ duration_ms: 82.158209
+ type: 'test'
+ ...
+# Subtest: retained nonuniform material preserves intervals and rejects missing replay metadata
+ok 307 - retained nonuniform material preserves intervals and rejects missing replay metadata
+ ---
+ duration_ms: 84.564875
+ type: 'test'
+ ...
+# Subtest: retained material rejects changed pinned alpha, positions and slab metadata
+ok 308 - retained material rejects changed pinned alpha, positions and slab metadata
+ ---
+ duration_ms: 49.503541
+ type: 'test'
+ ...
+# Subtest: retained material rejects missing XYZ and nonfinite sampler results
+ok 309 - retained material rejects missing XYZ and nonfinite sampler results
+ ---
+ duration_ms: 53.66825
+ type: 'test'
+ ...
+# Subtest: real prepared site profile replaces disks with a soft core/halo and conserves every color-channel light
+ok 310 - real prepared site profile replaces disks with a soft core/halo and conserves every color-channel light
+ ---
+ duration_ms: 19.836209
+ type: 'test'
+ ...
+# Subtest: sprite contract rejects missing colors, invalid atlas pins and fake photometric compensation
+ok 311 - sprite contract rejects missing colors, invalid atlas pins and fake photometric compensation
+ ---
+ duration_ms: 13.544709
+ type: 'test'
+ ...
+# Subtest: registered union adds a real infrared-only star, retains the optical anchor and deduplicates the shared source
+ok 312 - registered union adds a real infrared-only star, retains the optical anchor and deduplicates the shared source
+ ---
+ duration_ms: 25.249416
+ type: 'test'
+ ...
+# Subtest: source exposure normalization changes selection scores only and preserves actual per-lens measured light
+ok 313 - source exposure normalization changes selection scores only and preserves actual per-lens measured light
+ ---
+ duration_ms: 15.793708
+ type: 'test'
+ ...
+# Subtest: union configuration rejects missing sources, invalid tolerances and unsupported route instead of silently falling back
+ok 314 - union configuration rejects missing sources, invalid tolerances and unsupported route instead of silently falling back
+ ---
+ duration_ms: 1.049042
+ type: 'test'
+ ...
+# Subtest: prepared star display energy matches the observed residual without opacity or color floors
+ok 315 - prepared star display energy matches the observed residual without opacity or color floors
+ ---
+ duration_ms: 1.904458
+ type: 'test'
+ ...
+# Subtest: source intensity and angular image scale independently control light and footprint
+ok 316 - source intensity and angular image scale independently control light and footprint
+ ---
+ duration_ms: 2.111166
+ type: 'test'
+ ...
+# Subtest: local smooth residual background is excluded and neighboring apertures do not claim the same pixels
+ok 317 - local smooth residual background is excluded and neighboring apertures do not claim the same pixels
+ ---
+ duration_ms: 2.236292
+ type: 'test'
+ ...
+# Subtest: new angular footprints and historical screen footprints stay unambiguous
+ok 318 - new angular footprints and historical screen footprints stay unambiguous
+ ---
+ duration_ms: 0.190167
+ type: 'test'
+ ...
+# Subtest: registered infrared photometry changes appearance while the reference catalogue and XYZ remain fixed
+ok 319 - registered infrared photometry changes appearance while the reference catalogue and XYZ remain fixed
+ ---
+ duration_ms: 12.905583
+ type: 'test'
+ ...
+# Subtest: lens materials require complete known coverage and cannot carry geometry; historical stars remain readable
+ok 320 - lens materials require complete known coverage and cannot carry geometry; historical stars remain readable
+ ---
+ duration_ms: 0.177875
+ type: 'test'
+ ...
+# Subtest: the bounded catalogue favors broad bright residual energy over sharper faint peaks without changing stellar light or depth
+ok 321 - the bounded catalogue favors broad bright residual energy over sharper faint peaks without changing stellar light or depth
+ ---
+ duration_ms: 12.168875
+ type: 'test'
+ ...
+# Subtest: equal measured light keeps stable source identity order and weak positive columns retain field lights while zero columns do not
+ok 322 - equal measured light keeps stable source identity order and weak positive columns retain field lights while zero columns do not
+ ---
+ duration_ms: 15.774167
+ type: 'test'
+ ...
+# Subtest: more than 6000 sharper faint peaks cannot preempt a bright broad star before aperture ranking
+ok 323 - more than 6000 sharper faint peaks cannot preempt a bright broad star before aperture ranking
+ ---
+ duration_ms: 574.035542
+ type: 'test'
+ ...
+# Subtest: lower background subtraction recovers observed faint emission without elevating measured dark pixels
+ok 324 - lower background subtraction recovers observed faint emission without elevating measured dark pixels
+ ---
+ duration_ms: 30.525
+ type: 'test'
+ ...
+# Subtest: angular footprint reliability suppresses a source edge while retaining internal emission and no-data
+ok 325 - angular footprint reliability suppresses a source edge while retaining internal emission and no-data
+ ---
+ duration_ms: 17.912875
+ type: 'test'
+ ...
+# Subtest: the saved Lagoon recipe fades every source boundary and the common outer window while retaining interior light
+ok 326 - the saved Lagoon recipe fades every source boundary and the common outer window while retaining interior light
+ ---
+ duration_ms: 66.656667
+ type: 'test'
+ ...
+# Subtest: target controls reject unknown sources, malformed values and unsupported switches
+ok 327 - target controls reject unknown sources, malformed values and unsupported switches
+ ---
+ duration_ms: 0.322167
+ type: 'test'
+ ...
+# Subtest: an explicitly downweighted isolated footprint has no hidden normalized-mean brightness floor
+ok 328 - an explicitly downweighted isolated footprint has no hidden normalized-mean brightness floor
+ ---
+ duration_ms: 21.285583
+ type: 'test'
+ ...
+# Subtest: an authored rotated optical window trims all source contributions without cropping observed rasters
+ok 329 - an authored rotated optical window trims all source contributions without cropping observed rasters
+ ---
+ duration_ms: 16.741959
+ type: 'test'
+ ...
+# Subtest: bake arguments reject misspelled stages rather than unexpectedly running all processing
+ok 330 - bake arguments reject misspelled stages rather than unexpectedly running all processing
+ ---
+ duration_ms: 0.636
+ type: 'test'
+ ...
+# Subtest: image-filtered bakes require explicit research opt-in
+ok 331 - image-filtered bakes require explicit research opt-in
+ ---
+ duration_ms: 0.245833
+ type: 'test'
+ ...
+# Subtest: acquisition rejects altered local sources without downloading or overwriting them
+ok 332 - acquisition rejects altered local sources without downloading or overwriting them
+ ---
+ duration_ms: 7.577167
+ type: 'test'
+ ...
+# Subtest: the saved bake closes its inputs and preserves all three accepted material treatments
+ok 333 - the saved bake closes its inputs and preserves all three accepted material treatments
+ ---
+ duration_ms: 46.719625
+ type: 'test'
+ ...
+# Subtest: benchmark image loading rejects a changed source before decoding products
+ok 334 - benchmark image loading rejects a changed source before decoding products
+ ---
+ duration_ms: 6.909583
+ type: 'test'
+ ...
+# Subtest: product writer rejects a component-map reconstruction defect
+ok 335 - product writer rejects a component-map reconstruction defect
+ ---
+ duration_ms: 0.938667
+ type: 'test'
+ ...
+# Subtest: positive component over source black stays visible with declared neutral hue
+ok 336 - positive component over source black stays visible with declared neutral hue
+ ---
+ duration_ms: 9.70275
+ type: 'test'
+ ...
+# Subtest: neutral appearance is exact and a partial constant image gains no false photographic edge
+ok 337 - neutral appearance is exact and a partial constant image gains no false photographic edge
+ ---
+ duration_ms: 5.729125
+ type: 'test'
+ ...
+# Subtest: strength deepens a real dark feature, scale selects its surroundings, and no point is brightened
+ok 338 - strength deepens a real dark feature, scale selects its surroundings, and no point is brightened
+ ---
+ duration_ms: 2.968
+ type: 'test'
+ ...
+# Subtest: the real source-column gate rejects unresolved thin emission and admits converged integration
+ok 339 - the real source-column gate rejects unresolved thin emission and admits converged integration
+ ---
+ duration_ms: 8.096
+ type: 'test'
+ ...
+# Subtest: axis validation detects aliasing that a converged front column would not catch
+ok 340 - axis validation detects aliasing that a converged front column would not catch
+ ---
+ duration_ms: 1.841792
+ type: 'test'
+ ...
+# Subtest: every reference pixel keeps the photo-master RGBA optical column inside finite Z bounds
+ok 341 - every reference pixel keeps the photo-master RGBA optical column inside finite Z bounds
+ ---
+ duration_ms: 21.114125
+ type: 'test'
+ ...
+# Subtest: overlapping scales form one coherent family with one bounded depth support
+ok 342 - overlapping scales form one coherent family with one bounded depth support
+ ---
+ duration_ms: 0.421125
+ type: 'test'
+ ...
+# Subtest: stellar placement chooses a mode and does not average separated peaks into their empty gap
+ok 343 - stellar placement chooses a mode and does not average separated peaks into their empty gap
+ ---
+ duration_ms: 0.564125
+ type: 'test'
+ ...
+# Subtest: an edge mode that cannot contain the declared support is not shifted and claimed as prior placement
+ok 344 - an edge mode that cannot contain the declared support is not shifted and claimed as prior placement
+ ---
+ duration_ms: 0.6625
+ type: 'test'
+ ...
+# Subtest: declared parents must overlap in raster support
+ok 345 - declared parents must overlap in raster support
+ ---
+ duration_ms: 0.410875
+ type: 'test'
+ ...
+# Subtest: a full 512-square connected support reports its span without argument-spread overflow
+ok 346 - a full 512-square connected support reports its span without argument-spread overflow
+ ---
+ duration_ms: 46.054917
+ type: 'test'
+ ...
+# Subtest: an empty prior falls back to one deterministic common mode without per-pixel randomness
+ok 347 - an empty prior falls back to one deterministic common mode without per-pixel randomness
+ ---
+ duration_ms: 1.242875
+ type: 'test'
+ ...
+# Subtest: physical sample axes map left-to-right and max-Y-to-first-raster-row
+ok 348 - physical sample axes map left-to-right and max-Y-to-first-raster-row
+ ---
+ duration_ms: 0.135917
+ type: 'test'
+ ...
+# Subtest: RGB plus luminance follows the same shared-opacity column contract
+ok 349 - RGB plus luminance follows the same shared-opacity column contract
+ ---
+ duration_ms: 6.633042
+ type: 'test'
+ ...
+# Subtest: app preparation rebuilds missing prepared/test/slices/x/00.webp and rejects changed textures or reference metadata
+ok 350 - app preparation rebuilds missing prepared/test/slices/x/00.webp and rejects changed textures or reference metadata
+ ---
+ duration_ms: 81.9095
+ type: 'test'
+ ...
+# Subtest: app preparation rebuilds missing prepared/test/atlases/x.webp and rejects changed textures or reference metadata
+ok 351 - app preparation rebuilds missing prepared/test/atlases/x.webp and rejects changed textures or reference metadata
+ ---
+ duration_ms: 20.944167
+ type: 'test'
+ ...
+# Subtest: placed density projection samples original voxels through the inverse model transform
+ok 352 - placed density projection samples original voxels through the inverse model transform
+ ---
+ duration_ms: 2.067166
+ type: 'test'
+ ...
+# Subtest: morphology support rejects isolated field points and retains connected extended emission
+ok 353 - morphology support rejects isolated field points and retains connected extended emission
+ ---
+ duration_ms: 90.599833
+ type: 'test'
+ ...
+# Subtest: native extraction preserves resolution, alpha signal and source pins while omitting unwanted banks
+ok 354 - native extraction preserves resolution, alpha signal and source pins while omitting unwanted banks
+ ---
+ duration_ms: 161.252209
+ type: 'test'
+ ...
+# Subtest: every source pixel is retained by compact, extended, or diffuse output
+ok 355 - every source pixel is retained by compact, extended, or diffuse output
+ ---
+ duration_ms: 5.583833
+ type: 'test'
+ ...
+# Subtest: separated extended sources receive filled connected ownership instead of sparse peaks
+ok 356 - separated extended sources receive filled connected ownership instead of sparse peaks
+ ---
+ duration_ms: 1.073292
+ type: 'test'
+ ...
+# Subtest: reconstruction preserves an asymmetric source contour and its attached thin appendage
+ok 357 - reconstruction preserves an asymmetric source contour and its attached thin appendage
+ ---
+ duration_ms: 1.256125
+ type: 'test'
+ ...
+# Subtest: point sources remain explicit compact candidates beside extended emission
+ok 358 - point sources remain explicit compact candidates beside extended emission
+ ---
+ duration_ms: 0.759083
+ type: 'test'
+ ...
+# Subtest: reconstruction revisits an earlier thin contour after the initial raster queue drains
+ok 359 - reconstruction revisits an earlier thin contour after the initial raster queue drains
+ ---
+ duration_ms: 2.043167
+ type: 'test'
+ ...
+# Subtest: explicit analysis budgets stop oversized work without partial output
+ok 360 - explicit analysis budgets stop oversized work without partial output
+ ---
+ duration_ms: 0.229291
+ type: 'test'
+ ...
+# Subtest: extended contribution parts sum pointwise to the unchanged reference sampler
+ok 361 - extended contribution parts sum pointwise to the unchanged reference sampler
+ ---
+ duration_ms: 8.281792
+ type: 'test'
+ ...
+# Subtest: extended contribution optical columns sum exactly to the reference target
+ok 362 - extended contribution optical columns sum exactly to the reference target
+ ---
+ duration_ms: 1.0215
+ type: 'test'
+ ...
+# Subtest: compact and diffuse remain separately calibrated frozen-plane inspection channels
+ok 363 - compact and diffuse remain separately calibrated frozen-plane inspection channels
+ ---
+ duration_ms: 20.763625
+ type: 'test'
+ ...
+# Subtest: part preparation refuses a basis that would refit or renormalize the reference
+ok 364 - part preparation refuses a basis that would refit or renormalize the reference
+ ---
+ duration_ms: 0.227333
+ type: 'test'
+ ...
+# Subtest: native observation resampling retains color, orientation and uncovered sky
+ok 365 - native observation resampling retains color, orientation and uncovered sky
+ ---
+ duration_ms: 9.563458
+ type: 'test'
+ ...
+# Subtest: a source that declares no coverage keeps it through resampling and into the original overlay alpha
+ok 366 - a source that declares no coverage keeps it through resampling and into the original overlay alpha
+ ---
+ duration_ms: 21.348833
+ type: 'test'
+ ...
+# Subtest: projection gate detects a missing physical ray-length correction
+ok 367 - projection gate detects a missing physical ray-length correction
+ ---
+ duration_ms: 33.916709
+ type: 'test'
+ ...
+# Subtest: selected channels map to optical space after selection and retain their exact projected colour
+ok 368 - selected channels map to optical space after selection and retain their exact projected colour
+ ---
+ duration_ms: 13.370542
+ type: 'test'
+ ...
+# Subtest: broad mode follows distinct normalized prior columns without filling their empty middle
+ok 369 - broad mode follows distinct normalized prior columns without filling their empty middle
+ ---
+ duration_ms: 16.291334
+ type: 'test'
+ ...
+# Subtest: prior Y ascends in tangent space while photograph raster rows descend
+ok 370 - prior Y ascends in tangent space while photograph raster rows descend
+ ---
+ duration_ms: 0.223
+ type: 'test'
+ ...
+# Subtest: coherent families use separated prior modes through one bounded continuous depth field
+ok 371 - coherent families use separated prior modes through one bounded continuous depth field
+ ---
+ duration_ms: 0.567083
+ type: 'test'
+ ...
+# Subtest: extended supports are finite, continuous at their C1 boundary, and thicker inside a footprint
+ok 372 - extended supports are finite, continuous at their C1 boundary, and thicker inside a footprint
+ ---
+ duration_ms: 0.498125
+ type: 'test'
+ ...
+# Subtest: invalid pointwise decomposition accounting is rejected
+ok 373 - invalid pointwise decomposition accounting is rejected
+ ---
+ duration_ms: 0.243875
+ type: 'test'
+ ...
+# Subtest: coherent local profiles report conservative cropped support for an asymmetric depth plane
+ok 374 - coherent local profiles report conservative cropped support for an asymmetric depth plane
+ ---
+ duration_ms: 0.625834
+ type: 'test'
+ ...
+# Subtest: broad mode and coherent raw-prior diffuse light retain the full model support bounds
+ok 375 - broad mode and coherent raw-prior diffuse light retain the full model support bounds
+ ---
+ duration_ms: 0.2055
+ type: 'test'
+ ...
+# Subtest: one connected component inherits a continuous inclination fitted from supported prior columns
+ok 376 - one connected component inherits a continuous inclination fitted from supported prior columns
+ ---
+ duration_ms: 0.370417
+ type: 'test'
+ ...
+# Subtest: raw prior interpolation is continuous between cell centres and keeps a unit depth integral
+ok 377 - raw prior interpolation is continuous between cell centres and keeps a unit depth integral
+ ---
+ duration_ms: 7.344417
+ type: 'test'
+ ...
+# Subtest: FITS transport reverses DOM rows exactly once and preserves signed samples
+ok 378 - FITS transport reverses DOM rows exactly once and preserves signed samples
+ ---
+ duration_ms: 5.2705
+ type: 'test'
+ ...
+# Subtest: transport rejects missing pixels, float32 overflow and structural metadata overrides
+ok 379 - transport rejects missing pixels, float32 overflow and structural metadata overrides
+ ---
+ duration_ms: 0.607
+ type: 'test'
+ ...
+# Subtest: getsf input uses shared luminance, native pixel scale and complete official header/config contract
+ok 380 - getsf input uses shared luminance, native pixel scale and complete official header/config contract
+ ---
+ duration_ms: 12.816834
+ type: 'test'
+ ...
+# Subtest: signed official getsf maps reject default import and remain unchanged with explicit preview policy
+ok 381 - signed official getsf maps reject default import and remain unchanged with explicit preview policy
+ ---
+ duration_ms: 24.417917
+ type: 'test'
+ ...
+# Subtest: direct masters retain 64 slabs per axis, physical units, fine sampler detail and shared-opacity columns
+ok 382 - direct masters retain 64 slabs per axis, physical units, fine sampler detail and shared-opacity columns
+ ---
+ duration_ms: 683.172708
+ type: 'test'
+ ...
+# Subtest: delivery reads pinned PNG masters, averages premultiplied color and maps cropped pixel edges into physical quads
+ok 383 - delivery reads pinned PNG masters, averages premultiplied color and maps cropped pixel edges into physical quads
+ ---
+ duration_ms: 64.627333
+ type: 'test'
+ ...
+# Subtest: delivery height uses physical aspect before rounding and invalid field samples are rejected
+ok 384 - delivery height uses physical aspect before rounding and invalid field samples are rejected
+ ---
+ duration_ms: 15.385041
+ type: 'test'
+ ...
+# Subtest: faint optical light survives 48/96 slab quantization with the same integrated brightness and hue
+ok 385 - faint optical light survives 48/96 slab quantization with the same integrated brightness and hue
+ ---
+ duration_ms: 469.4575
+ type: 'test'
+ ...
+# Subtest: RGB error carry preserves faint colored layers and never deposits residuals in empty support
+ok 386 - RGB error carry preserves faint colored layers and never deposits residuals in empty support
+ ---
+ duration_ms: 309.514916
+ type: 'test'
+ ...
+# Subtest: photo emission preserves column signal while particles determine depth
+ok 387 - photo emission preserves column signal while particles determine depth
+ ---
+ duration_ms: 1.11475
+ type: 'test'
+ ...
+# Subtest: optional local contrast narrows detail without changing column emission
+ok 388 - optional local contrast narrows detail without changing column emission
+ ---
+ duration_ms: 0.716042
+ type: 'test'
+ ...
+# Subtest: photo emission softly gates image edges and low-density tails
+ok 389 - photo emission softly gates image edges and low-density tails
+ ---
+ duration_ms: 0.196709
+ type: 'test'
+ ...
+# Subtest: photo emission rejects invalid density instead of producing non-finite output
+ok 390 - photo emission rejects invalid density instead of producing non-finite output
+ ---
+ duration_ms: 0.296
+ type: 'test'
+ ...
+# Subtest: master sampler retains photo frequencies finer than its depth grid
+ok 391 - master sampler retains photo frequencies finer than its depth grid
+ ---
+ duration_ms: 1.201708
+ type: 'test'
+ ...
+# Subtest: master sampler conserves optical columns and gates empty density
+ok 392 - master sampler conserves optical columns and gates empty density
+ ---
+ duration_ms: 0.194083
+ type: 'test'
+ ...
+# Subtest: compact model placement reuses exact density/alpha bytes and restores missing generated resources
+ok 393 - compact model placement reuses exact density/alpha bytes and restores missing generated resources
+ ---
+ duration_ms: 31.700625
+ type: 'test'
+ ...
+# Subtest: processing setup reads the canonical package and NOX pins without object asset dependencies
+ok 394 - processing setup reads the canonical package and NOX pins without object asset dependencies
+ ---
+ duration_ms: 3.178375
+ type: 'test'
+ ...
+# Subtest: processing setup rejects missing pins, unsupported Python and unpinned package specifications
+ok 395 - processing setup rejects missing pins, unsupported Python and unpinned package specifications
+ ---
+ duration_ms: 0.199625
+ type: 'test'
+ ...
+# Subtest: all 943 stars follow the fixed reference-image fit and occupy the unchanged source density
+ok 396 - all 943 stars follow the fixed reference-image fit and occupy the unchanged source density
+ ---
+ duration_ms: 1249.604417
+ type: 'test'
+ ...
+# Subtest: wrong reference/density, unsupported model rays and candidate-image inputs reject instead of placing or filtering stars
+ok 397 - wrong reference/density, unsupported model rays and candidate-image inputs reject instead of placing or filtering stars
+ ---
+ duration_ms: 196.985875
+ type: 'test'
+ ...
+# Subtest: the common density signal and all-light membership retain star toggle and cutoff behavior
+ok 398 - the common density signal and all-light membership retain star toggle and cutoff behavior
+ ---
+ duration_ms: 121.88625
+ type: 'test'
+ ...
+# Subtest: verification fails for missing or changed outputs and never repairs them
+ok 399 - verification fails for missing or changed outputs and never repairs them
+ ---
+ duration_ms: 40.854584
+ type: 'test'
+ ...
+# Subtest: structure input cannot silently rerun star removal when a native receipt is absent
+ok 400 - structure input cannot silently rerun star removal when a native receipt is absent
+ ---
+ duration_ms: 9.528625
+ type: 'test'
+ ...
+# Subtest: structure recipe bounds the analysis without changing source footprint
+ok 401 - structure recipe bounds the analysis without changing source footprint
+ ---
+ duration_ms: 0.270875
+ type: 'test'
+ ...
+# Subtest: highlight sprites contain actual disconnected support, never filled bounding rectangles
+ok 402 - highlight sprites contain actual disconnected support, never filled bounding rectangles
+ ---
+ duration_ms: 0.733416
+ type: 'test'
+ ...
+# Subtest: resized support preserves the exact full native image edges and registration
+ok 403 - resized support preserves the exact full native image edges and registration
+ ---
+ duration_ms: 0.111333
+ type: 'test'
+ ...
+# Subtest: atlas packing rejects support outside the real source footprint
+ok 404 - atlas packing rejects support outside the real source footprint
+ ---
+ duration_ms: 0.159834
+ type: 'test'
+ ...
+# Subtest: registration preserves asymmetric landmarks under native resampling, rotation and translation
+ok 405 - registration preserves asymmetric landmarks under native resampling, rotation and translation
+ ---
+ duration_ms: 0.826541
+ type: 'test'
+ ...
+# Subtest: registered grid retains adjusted footprints outside the original common frame
+ok 406 - registered grid retains adjusted footprints outside the original common frame
+ ---
+ duration_ms: 0.116417
+ type: 'test'
+ ...
+# Subtest: different working resolutions retain the same angular ridge location after registration and common-scale smoothing
+ok 407 - different working resolutions retain the same angular ridge location after registration and common-scale smoothing
+ ---
+ duration_ms: 66.342291
+ type: 'test'
+ ...
+# Subtest: crop boundaries and internal no-data holes do not create ridges or compact structures
+ok 408 - crop boundaries and internal no-data holes do not create ridges or compact structures
+ ---
+ duration_ms: 14.0715
+ type: 'test'
+ ...
+# Subtest: source-local normalization is stable under fixed brightness and additive background differences
+ok 409 - source-local normalization is stable under fixed brightness and additive background differences
+ ---
+ duration_ms: 110.702292
+ type: 'test'
+ ...
+# Subtest: single-band structures survive blank or uncovered other bands; repeat evidence has separate agreement
+ok 410 - single-band structures survive blank or uncovered other bands; repeat evidence has separate agreement
+ ---
+ duration_ms: 67.622375
+ type: 'test'
+ ...
+# Subtest: crossed ridge tangents are not repeated compatible evidence
+ok 411 - crossed ridge tangents are not repeated compatible evidence
+ ---
+ duration_ms: 42.01175
+ type: 'test'
+ ...
+# Subtest: all-channel agreement cannot pair different morphology channels
+ok 412 - all-channel agreement cannot pair different morphology channels
+ ---
+ duration_ms: 105.246625
+ type: 'test'
+ ...
+# Subtest: channel and source controls change fields without manufacturing coverage
+ok 413 - channel and source controls change fields without manufacturing coverage
+ ---
+ duration_ms: 83.106583
+ type: 'test'
+ ...
+# Subtest: real pinned Helix inputs produce finite reproducible registered evidence with cached reuse
+ok 414 - real pinned Helix inputs produce finite reproducible registered evidence with cached reuse # SKIP
+ ---
+ duration_ms: 0.303875
+ type: 'test'
+ ...
+# Subtest: real three-source ridge graph preserves every sampled footprint and projects onto the registered source
+ok 415 - real three-source ridge graph preserves every sampled footprint and projects onto the registered source # SKIP
+ ---
+ duration_ms: 0.245
+ type: 'test'
+ ...
+# Subtest: joint volume samples caller arcseconds and prepares nonempty retained XYZ stacks
+ok 416 - joint volume samples caller arcseconds and prepares nonempty retained XYZ stacks
+ ---
+ duration_ms: 913.002542
+ type: 'test'
+ ...
+# Subtest: joint volume rejects invalid bounds and an already-cancelled bake before sampling
+ok 417 - joint volume rejects invalid bounds and an already-cancelled bake before sampling
+ ---
+ duration_ms: 1.110208
+ type: 'test'
+ ...
+# Subtest: sphere sightlines satisfy the analytic chord, tangent and outside cases regardless of orientation
+ok 418 - sphere sightlines satisfy the analytic chord, tangent and outside cases regardless of orientation
+ ---
+ duration_ms: 0.89075
+ type: 'test'
+ ...
+# Subtest: sky position angle uses west/north axes and positive depth produces redshift in known homologous expansion
+ok 419 - sky position angle uses west/north axes and positive depth produces redshift in known homologous expansion
+ ---
+ duration_ms: 2.682625
+ type: 'test'
+ ...
+# Subtest: bipolar polar ray includes both exact outer endpoints and reflection preserves all four resolved crossings
+ok 420 - bipolar polar ray includes both exact outer endpoints and reflection preserves all four resolved crossings
+ ---
+ duration_ms: 0.571708
+ type: 'test'
+ ...
+# Subtest: fit objective ignores held-out dependent values while their diagnostic residuals change
+ok 421 - fit objective ignores held-out dependent values while their diagnostic residuals change
+ ---
+ duration_ms: 0.718791
+ type: 'test'
+ ...
+# Subtest: held-out velocities cannot change bounded-fit parameters, objective or family ranking
+ok 422 - held-out velocities cannot change bounded-fit parameters, objective or family ranking
+ ---
+ duration_ms: 126.099333
+ type: 'test'
+ ...
+# Subtest: missing modeled training rays incur the configured penalty instead of disappearing from the objective
+ok 423 - missing modeled training rays incur the configured penalty instead of disappearing from the objective
+ ---
+ duration_ms: 0.673375
+ type: 'test'
+ ...
+# Subtest: ellipsoid outline matches the independent projected ellipse and tangent rays at every image angle
+ok 424 - ellipsoid outline matches the independent projected ellipse and tangent rays at every image angle
+ ---
+ duration_ms: 0.417333
+ type: 'test'
+ ...
+# Subtest: bipolar outline is a surface envelope with line-of-sight tangency, including competing lobe maxima
+ok 425 - bipolar outline is a surface envelope with line-of-sight tangency, including competing lobe maxima
+ ---
+ duration_ms: 10.234209
+ type: 'test'
+ ...
+# Subtest: runtime controls reject both objectives disabled and nonfinite values while either objective may be disabled alone
+ok 426 - runtime controls reject both objectives disabled and nonfinite values while either objective may be disabled alone
+ ---
+ duration_ms: 0.579584
+ type: 'test'
+ ...
+# Subtest: a sphere gives the analytical position-velocity ellipse and no emission beyond its boundary
+ok 427 - a sphere gives the analytical position-velocity ellipse and no emission beyond its boundary
+ ---
+ duration_ms: 0.555584
+ type: 'test'
+ ...
+# Subtest: spherical inclination is unidentifiable; nonspherical inclination changes the asymmetric slit tilt
+ok 428 - spherical inclination is unidentifiable; nonspherical inclination changes the asymmetric slit tilt
+ ---
+ duration_ms: 0.173333
+ type: 'test'
+ ...
+# Subtest: projected extent stays fixed under depth and inclination; speed scales physical predictions
+ok 429 - projected extent stays fixed under depth and inclination; speed scales physical predictions
+ ---
+ duration_ms: 0.070167
+ type: 'test'
+ ...
+# Subtest: real source pixels calibrate to heliocentric velocities and systemic velocity is subtracted exactly once
+ok 430 - real source pixels calibrate to heliocentric velocities and systemic velocity is subtracted exactly once
+ ---
+ duration_ms: 1.785916
+ type: 'test'
+ ...
+# Subtest: readout uncertainty is separate from systemic uncertainty and not instrumental slit width
+ok 431 - readout uncertainty is separate from systemic uncertainty and not instrumental slit width
+ ---
+ duration_ms: 0.870125
+ type: 'test'
+ ...
+# Subtest: external values reject corrupt calibration, unknown frames, missing observations, and nonfinite assumptions
+ok 432 - external values reject corrupt calibration, unknown frames, missing observations, and nonfinite assumptions
+ ---
+ duration_ms: 1.483209
+ type: 'test'
+ ...
+# Subtest: source identity follows actual recipe bytes and model routes refuse non-model input
+ok 433 - source identity follows actual recipe bytes and model routes refuse non-model input
+ ---
+ duration_ms: 3.695542
+ type: 'test'
+ ...
+# Subtest: a real HTTP handoff returns validated measured points, recomputes slider predictions and preserves observed velocities
+ok 434 - a real HTTP handoff returns validated measured points, recomputes slider predictions and preserves observed velocities
+ ---
+ duration_ms: 24.3265
+ type: 'test'
+ ...
+# Subtest: publisher ASCII85 decoding preserves exact JPEG bytes and rejects incomplete source streams
+ok 435 - publisher ASCII85 decoding preserves exact JPEG bytes and rejects incomplete source streams
+ ---
+ duration_ms: 0.490083
+ type: 'test'
+ ...
+# Subtest: published offset signs and exact origin are retained without a second cosine factor or hidden recentering
+ok 436 - published offset signs and exact origin are retained without a second cosine factor or hidden recentering
+ ---
+ duration_ms: 1.700875
+ type: 'test'
+ ...
+# Subtest: published LSR centroids remain untouched despite the available source-specific heliocentric conversion
+ok 437 - published LSR centroids remain untouched despite the available source-specific heliocentric conversion
+ ---
+ duration_ms: 0.361959
+ type: 'test'
+ ...
+# Subtest: multiple components preserve the common pointing key and their own measured widths and velocities
+ok 438 - multiple components preserve the common pointing key and their own measured widths and velocities
+ ---
+ duration_ms: 0.3995
+ type: 'test'
+ ...
+# Subtest: upper limits remain intensity constraints with null velocity, width, errors, and inferred chemistry
+ok 439 - upper limits remain intensity constraints with null velocity, width, errors, and inferred chemistry
+ ---
+ duration_ms: 0.266833
+ type: 'test'
+ ...
+# Subtest: chemistry stays a separate inferred quantity and broad lines are marked without dropping observations
+ok 440 - chemistry stays a separate inferred quantity and broad lines are marked without dropping observations
+ ---
+ duration_ms: 0.366083
+ type: 'test'
+ ...
+# Subtest: malformed fields, incompatible frames, double-applied conversion and contradictory counts fail validation
+ok 441 - malformed fields, incompatible frames, double-applied conversion and contradictory counts fail validation
+ ---
+ duration_ms: 0.624459
+ type: 'test'
+ ...
+# Subtest: explicit acquisition verifies actual side effects, then replay uses the source cache without any fetch
+ok 442 - explicit acquisition verifies actual side effects, then replay uses the source cache without any fetch
+ ---
+ duration_ms: 38.06525
+ type: 'test'
+ ...
+# Subtest: HTTP 200 without the pinned bytes does not establish source acquisition
+ok 443 - HTTP 200 without the pinned bytes does not establish source acquisition
+ ---
+ duration_ms: 7.827333
+ type: 'test'
+ ...
+# Subtest: actual inspected native identities pass unchanged affine gates and reject shift, parity and scale corruption
+ok 444 - actual inspected native identities pass unchanged affine gates and reject shift, parity and scale corruption
+ ---
+ duration_ms: 33.714208
+ type: 'test'
+ ...
+# Subtest: full published native grids receive a larger bounded work budget than small previews
+ok 445 - full published native grids receive a larger bounded work budget than small previews
+ ---
+ duration_ms: 0.339834
+ type: 'test'
+ ...
+# Subtest: alignment-only missing or invalid receipt cannot launch NOX or create a worker request
+ok 446 - alignment-only missing or invalid receipt cannot launch NOX or create a worker request
+ ---
+ duration_ms: 10.793
+ type: 'test'
+ ...
+# Subtest: preservation keeps compact emission exactly and needs no NOX model or worker
+ok 447 - preservation keeps compact emission exactly and needs no NOX model or worker
+ ---
+ duration_ms: 17.447167
+ type: 'test'
+ ...
+# Subtest: the mixed M45 intake explicitly reuses exactly its four completed native sources
+ok 448 - the mixed M45 intake explicitly reuses exactly its four completed native sources
+ ---
+ duration_ms: 3.998375
+ type: 'test'
+ ...
+# Subtest: reuse rejects changed source bytes, grids, treatment and model/script signatures
+ok 449 - reuse rejects changed source bytes, grids, treatment and model/script signatures
+ ---
+ duration_ms: 1.668375
+ type: 'test'
+ ...
+# Subtest: cache recipes require immutable pins and bounded repository paths
+ok 450 - cache recipes require immutable pins and bounded repository paths
+ ---
+ duration_ms: 1.253791
+ type: 'test'
+ ...
+# Subtest: real common-grid transfer pins both originals and cannot impersonate stellar verification
+ok 451 - real common-grid transfer pins both originals and cannot impersonate stellar verification
+ ---
+ duration_ms: 4.3095
+ type: 'test'
+ ...
+# Subtest: transfer graph rejects cycles, missing references, and singular transforms
+ok 452 - transfer graph rejects cycles, missing references, and singular transforms
+ ---
+ duration_ms: 1.41075
+ type: 'test'
+ ...
+# Subtest: native AVM reference pixel, not raster centre, anchors the sky coordinates
+ok 453 - native AVM reference pixel, not raster centre, anchors the sky coordinates
+ ---
+ duration_ms: 0.619833
+ type: 'test'
+ ...
+# Subtest: Omega Centauri native stars reject the former centred AVM reference pixels
+ok 454 - Omega Centauri native stars reject the former centred AVM reference pixels
+ ---
+ duration_ms: 16.472875
+ type: 'test'
+ ...
+# Subtest: Gaussian high-pass keeps channels aligned and finds actual compact sources
+ok 455 - Gaussian high-pass keeps channels aligned and finds actual compact sources
+ ---
+ duration_ms: 6.780583
+ type: 'test'
+ ...
+# Subtest: spatially held-out stars pass a correct affine and retain an incorrect held-out identity
+ok 456 - spatially held-out stars pass a correct affine and retain an incorrect held-out identity
+ ---
+ duration_ms: 6.787458
+ type: 'test'
+ ...
+# Subtest: observation recipe rejects unpinned and malformed sky inputs
+ok 457 - observation recipe rejects unpinned and malformed sky inputs
+ ---
+ duration_ms: 1.336917
+ type: 'test'
+ ...
+# Subtest: registration measures coverage over the actual shared footprint, preserving held-out error gates
+ok 458 - registration measures coverage over the actual shared footprint, preserving held-out error gates
+ ---
+ duration_ms: 11.045458
+ type: 'test'
+ ...
+# Subtest: sampled final scene prepares every spectral star color after merging materials and retains the neutral atlas
+ok 459 - sampled final scene prepares every spectral star color after merging materials and retains the neutral atlas
+ ---
+ duration_ms: 31.397667
+ type: 'test'
+ ...
+# Subtest: registration preserves the full planned partition until compilation prunes truly empty slabs
+ok 460 - registration preserves the full planned partition until compilation prunes truly empty slabs
+ ---
+ duration_ms: 70.255458
+ type: 'test'
+ ...
+# Subtest: component registration rejects catalogue intervals that disagree with the actual retained plan
+ok 461 - component registration rejects catalogue intervals that disagree with the actual retained plan
+ ---
+ duration_ms: 54.455666
+ type: 'test'
+ ...
+# Subtest: an allegedly pruned slab with real opacity cannot be reintroduced as transparent metadata
+ok 462 - an allegedly pruned slab with real opacity cannot be reintroduced as transparent metadata
+ ---
+ duration_ms: 53.913916
+ type: 'test'
+ ...
+# Subtest: finite 3D atom projects to its numerically integrated emission and tapers in depth
+ok 463 - finite 3D atom projects to its numerically integrated emission and tapers in depth
+ ---
+ duration_ms: 3.549875
+ type: 'test'
+ ...
+# Subtest: fixed 3D supports stay inside the field and gridding adds finite nonnegative emission
+ok 464 - fixed 3D supports stay inside the field and gridding adds finite nonnegative emission
+ ---
+ duration_ms: 33.2665
+ type: 'test'
+ ...
+# Subtest: image fitting improves withheld and independently sampled pixels without moving measured or inferred geometry
+ok 465 - image fitting improves withheld and independently sampled pixels without moving measured or inferred geometry
+ ---
+ duration_ms: 144.331125
+ type: 'test'
+ ...
+# Subtest: sampled recipe rejects malformed fitting controls and unowned source/evidence identities
+ok 466 - sampled recipe rejects malformed fitting controls and unowned source/evidence identities
+ ---
+ duration_ms: 0.967458
+ type: 'test'
+ ...
+# Subtest: insufficient image coverage and malformed covered pixels fail before publishing fitted material
+ok 467 - insufficient image coverage and malformed covered pixels fail before publishing fitted material
+ ---
+ duration_ms: 30.23925
+ type: 'test'
+ ...
+# Subtest: shifted spectral crops preserve every RGBA byte on all three oriented planes
+ok 468 - shifted spectral crops preserve every RGBA byte on all three oriented planes
+ ---
+ duration_ms: 1.286875
+ type: 'test'
+ ...
+# Subtest: same-size component is copied exactly and missing or transparent planes remain transparent
+ok 469 - same-size component is copied exactly and missing or transparent planes remain transparent
+ ---
+ duration_ms: 0.159292
+ type: 'test'
+ ...
+# Subtest: nonempty pixels outside any crop edge fail instead of being clipped
+ok 470 - nonempty pixels outside any crop edge fail instead of being clipped
+ ---
+ duration_ms: 0.24725
+ type: 'test'
+ ...
+# Subtest: padding rejects resampling, reversed rows, noninteger offsets and another physical plane
+ok 471 - padding rejects resampling, reversed rows, noninteger offsets and another physical plane
+ ---
+ duration_ms: 0.104166
+ type: 'test'
+ ...
+# Subtest: plane identities and decoded raster dimensions must match
+ok 472 - plane identities and decoded raster dimensions must match
+ ---
+ duration_ms: 0.1725
+ type: 'test'
+ ...
+# Subtest: actual footprint union expands every edge on each oriented pixel grid without modifying inputs
+ok 473 - actual footprint union expands every edge on each oriented pixel grid without modifying inputs
+ ---
+ duration_ms: 0.728791
+ type: 'test'
+ ...
+# Subtest: a quantized component row beyond neutral survives in the merged layout
+ok 474 - a quantized component row beyond neutral survives in the merged layout
+ ---
+ duration_ms: 0.154583
+ type: 'test'
+ ...
+# Subtest: footprint union cannot merge different slabs, resampled pixels or offsets outside the shared grid
+ok 475 - footprint union cannot merge different slabs, resampled pixels or offsets outside the shared grid
+ ---
+ duration_ms: 0.10375
+ type: 'test'
+ ...
+# Subtest: registered footprint union retains physical slab intervals and rejects mismatched integration support
+ok 476 - registered footprint union retains physical slab intervals and rejects mismatched integration support
+ ---
+ duration_ms: 0.334375
+ type: 'test'
+ ...
+# Subtest: qualified point colors remain at finite source depths where sky footprints overlap; XY extrusion fails
+ok 477 - qualified point colors remain at finite source depths where sky footprints overlap; XY extrusion fails
+ ---
+ duration_ms: 11.541459
+ type: 'test'
+ ...
+# Subtest: uncovered finite emitters retain neutral contributions and explicit coverage
+ok 478 - uncovered finite emitters retain neutral contributions and explicit coverage
+ ---
+ duration_ms: 9.426584
+ type: 'test'
+ ...
+# Subtest: a finite diffuse atom owns one footprint color instead of repeating an image feature through its depth
+ok 479 - a finite diffuse atom owns one footprint color instead of repeating an image feature through its depth
+ ---
+ duration_ms: 8.725583
+ type: 'test'
+ ...
+# Subtest: invalid material fit lengths and invalid observed color fail before transport
+ok 480 - invalid material fit lengths and invalid observed color fail before transport
+ ---
+ duration_ms: 4.771708
+ type: 'test'
+ ...
+# Subtest: compact emitter colors regenerate the identical material without source images
+ok 481 - compact emitter colors regenerate the identical material without source images
+ ---
+ duration_ms: 6.273375
+ type: 'test'
+ ...
+# Subtest: comparison projects actual 3D RGB, not the photograph used as its target
+ok 482 - comparison projects actual 3D RGB, not the photograph used as its target
+ ---
+ duration_ms: 9.778375
+ type: 'test'
+ ...
+# Subtest: sampled bank progress never reverses when baker phase counters restart
+ok 483 - sampled bank progress never reverses when baker phase counters restart
+ ---
+ duration_ms: 0.616084
+ type: 'test'
+ ...
+# Subtest: qualified affine transform retains parity, anisotropic scales, offsets and away direction
+ok 484 - qualified affine transform retains parity, anisotropic scales, offsets and away direction
+ ---
+ duration_ms: 1.745
+ type: 'test'
+ ...
+# Subtest: source and evidence byte pins, dimensions and physical term identities are mandatory
+ok 485 - source and evidence byte pins, dimensions and physical term identities are mandatory
+ ---
+ duration_ms: 0.534709
+ type: 'test'
+ ...
+# Subtest: analytic terms and the named pulsar must reference evidence for this object
+ok 486 - analytic terms and the named pulsar must reference evidence for this object
+ ---
+ duration_ms: 0.238042
+ type: 'test'
+ ...
+# Subtest: sampled evidence rejects invalid classifications and unresolvable source attribution
+ok 487 - sampled evidence rejects invalid classifications and unresolvable source attribution
+ ---
+ duration_ms: 0.191125
+ type: 'test'
+ ...
+# Subtest: torus emission follows its declared plane and finite tube, not a spherical shell
+ok 488 - torus emission follows its declared plane and finite tube, not a spherical shell
+ ---
+ duration_ms: 0.241542
+ type: 'test'
+ ...
+# Subtest: jet orientation follows both endpoints with finite end caps
+ok 489 - jet orientation follows both endpoints with finite end caps
+ ---
+ duration_ms: 0.150958
+ type: 'test'
+ ...
+# Subtest: decoded FITS point splats preserve all positive extent and normalized mass support
+ok 490 - decoded FITS point splats preserve all positive extent and normalized mass support
+ ---
+ duration_ms: 6.509541
+ type: 'test'
+ ...
+# Subtest: two clouds on one sightline keep their empty intervening depth instead of photo extrusion
+ok 491 - two clouds on one sightline keep their empty intervening depth instead of photo extrusion
+ ---
+ duration_ms: 0.7555
+ type: 'test'
+ ...
+# Subtest: independent wind terms never alter the ejecta array and spectral mixtures remain separate
+ok 492 - independent wind terms never alter the ejecta array and spectral mixtures remain separate
+ ---
+ duration_ms: 15.916917
+ type: 'test'
+ ...
+# Subtest: nonfinite point flux cannot contaminate an otherwise valid qualified field
+ok 493 - nonfinite point flux cannot contaminate an otherwise valid qualified field
+ ---
+ duration_ms: 0.371542
+ type: 'test'
+ ...
+# Subtest: sampled cache verifies its shared FITS decoder without admitting arbitrary tools
+ok 494 - sampled cache verifies its shared FITS decoder without admitting arbitrary tools
+ ---
+ duration_ms: 6.9515
+ type: 'test'
+ ...
+# Subtest: partial ring preserves supported emission, softens its ends and removes the unobserved opposite side
+ok 495 - partial ring preserves supported emission, softens its ends and removes the unobserved opposite side
+ ---
+ duration_ms: 0.7355
+ type: 'test'
+ ...
+# Subtest: complete rings retain legacy behavior and clockwise sectors wrap at the image angle seam
+ok 496 - complete rings retain legacy behavior and clockwise sectors wrap at the image angle seam
+ ---
+ duration_ms: 1.097834
+ type: 'test'
+ ...
+# Subtest: source registration maps the complete baked neutral projection with the original Y direction
+ok 497 - source registration maps the complete baked neutral projection with the original Y direction
+ ---
+ duration_ms: 2.726958
+ type: 'test'
+ ...
+# Subtest: comparison fits only one global brightness factor and detects a moved shape
+ok 498 - comparison fits only one global brightness factor and detects a moved shape
+ ---
+ duration_ms: 7.011
+ type: 'test'
+ ...
+# Subtest: faint outer source signal remains in the residual beyond every modeled component
+ok 499 - faint outer source signal remains in the residual beyond every modeled component
+ ---
+ duration_ms: 1.768083
+ type: 'test'
+ ...
+# Subtest: empty models retain the whole missing signal and black inputs do not manufacture a fit
+ok 500 - empty models retain the whole missing signal and black inputs do not manufacture a fit
+ ---
+ duration_ms: 1.414459
+ type: 'test'
+ ...
+# Subtest: canonical expression roundtrips signed weights without changing geometry, grouping or exposure
+ok 501 - canonical expression roundtrips signed weights without changing geometry, grouping or exposure
+ ---
+ duration_ms: 1.232042
+ type: 'test'
+ ...
+# Subtest: aliases stay stable across disabled shapes, zero clears all, and a negative first term is valid
+ok 502 - aliases stay stable across disabled shapes, zero clears all, and a negative first term is valid
+ ---
+ duration_ms: 0.264375
+ type: 'test'
+ ...
+# Subtest: duplicate aliases combine before net bounds are checked, cancellation disables, small finite coefficients survive
+ok 503 - duplicate aliases combine before net bounds are checked, cancellation disables, small finite coefficients survive
+ ---
+ duration_ms: 0.755583
+ type: 'test'
+ ...
+# Subtest: unknown aliases, malformed expressions, executable text and nonfinite/out-of-range coefficients are rejected
+ok 504 - unknown aliases, malformed expressions, executable text and nonfinite/out-of-range coefficients are rejected
+ ---
+ duration_ms: 0.231917
+ type: 'test'
+ ...
+# Subtest: saved fits keep their authored values but cannot attach to different source evidence
+ok 505 - saved fits keep their authored values but cannot attach to different source evidence
+ ---
+ duration_ms: 1.055667
+ type: 'test'
+ ...
+# Subtest: invalid fit settings and unsafe paths fail before a fit enters the viewer
+ok 506 - invalid fit settings and unsafe paths fail before a fit enters the viewer
+ ---
+ duration_ms: 0.39325
+ type: 'test'
+ ...
+# Subtest: near duplicate boundaries seed one shell without multiplying its weight; distinct nested/off-center shapes remain
+ok 507 - near duplicate boundaries seed one shell without multiplying its weight; distinct nested/off-center shapes remain
+ ---
+ duration_ms: 6.57325
+ type: 'test'
+ ...
+# Subtest: shape control parser rejects nonfinite values, duplicate ownership, unknown controls and out-of-range dimensions
+ok 508 - shape control parser rejects nonfinite values, duplicate ownership, unknown controls and out-of-range dimensions
+ ---
+ duration_ms: 0.697209
+ type: 'test'
+ ...
+# Subtest: raster rotations, asymmetric placement and source pixel centers share one x-right/y-up projection
+ok 509 - raster rotations, asymmetric placement and source pixel centers share one x-right/y-up projection
+ ---
+ duration_ms: 1.349625
+ type: 'test'
+ ...
+# Subtest: weight, thickness, softness and depth alter the field while baking bounds retain complete emission without empty photo margins
+ok 510 - weight, thickness, softness and depth alter the field while baking bounds retain complete emission without empty photo margins
+ ---
+ duration_ms: 0.45175
+ type: 'test'
+ ...
+# Subtest: anisotropic clouds use one physical slice spacing rather than the same slab count on each axis
+ok 511 - anisotropic clouds use one physical slice spacing rather than the same slab count on each axis
+ ---
+ duration_ms: 0.126917
+ type: 'test'
+ ...
+# Subtest: 3D rings keep their opening through depth; signed terms carve soft cavities without negative emission
+ok 512 - 3D rings keep their opening through depth; signed terms carve soft cavities without negative emission
+ ---
+ duration_ms: 0.338292
+ type: 'test'
+ ...
+# Subtest: actual XYZ bake paints the exact neutral alpha and geometry, records pins, responds to exposure and handles empty controls
+ok 513 - actual XYZ bake paints the exact neutral alpha and geometry, records pins, responds to exposure and handles empty controls
+ ---
+ duration_ms: 12258.064625
+ type: 'test'
+ ...
+# Subtest: measured bright HIP survives missing Tycho astrometry, wins identity, and keeps blue-white measured color
+ok 514 - measured bright HIP survives missing Tycho astrometry, wins identity, and keeps blue-white measured color
+ ---
+ duration_ms: 1.498708
+ type: 'test'
+ ...
+# Subtest: scientific pin survives changed response timestamp but rejects changed photometry
+ok 515 - scientific pin survives changed response timestamp but rejects changed photometry
+ ---
+ duration_ms: 0.152416
+ type: 'test'
+ ...
+# Subtest: error responses, missing columns, invalid numbers and duplicate HIP IDs fail
+ok 516 - error responses, missing columns, invalid numbers and duplicate HIP IDs fail
+ ---
+ duration_ms: 0.205
+ type: 'test'
+ ...
+# Subtest: the shared placement replays the prepared LMC envelope layer deterministically in the model frame
+ok 517 - the shared placement replays the prepared LMC envelope layer deterministically in the model frame # SKIP The local finite LMC model named by the prepared envelope stars is not present.
+ ---
+ duration_ms: 0.373166
+ type: 'test'
+ ...
+# Subtest: selection is finite V <= 16 on covered fit pixels, and the footprint predicate is load-bearing
+ok 518 - selection is finite V <= 16 on covered fit pixels, and the footprint predicate is load-bearing # SKIP The local finite LMC model named by the prepared envelope stars is not present.
+ ---
+ duration_ms: 0.054292
+ type: 'test'
+ ...
+# Subtest: every prepared LMC star sits on positive model emission, positive envelope density and inside the baked box
+ok 519 - every prepared LMC star sits on positive model emission, positive envelope density and inside the baked box # SKIP The local finite LMC model named by the prepared envelope stars is not present.
+ ---
+ duration_ms: 0.0425
+ type: 'test'
+ ...
+# Subtest: the joint CDF guards reject rays without emission or density and never bridge a zero-support gap
+ok 520 - the joint CDF guards reject rays without emission or density and never bridge a zero-support gap # SKIP The local finite LMC model named by the prepared envelope stars is not present.
+ ---
+ duration_ms: 0.036125
+ type: 'test'
+ ...
+# Subtest: the model-owned index is what lens discovery attaches, and it is pinned to these bytes and this subject
+ok 521 - the model-owned index is what lens discovery attaches, and it is pinned to these bytes and this subject # SKIP The local finite LMC model named by the prepared envelope stars is not present.
+ ---
+ duration_ms: 0.0975
+ type: 'test'
+ ...
+# Subtest: one owner serves both bodies, and each body keeps its own catalogue columns and layer paths
+ok 522 - one owner serves both bodies, and each body keeps its own catalogue columns and layer paths # SKIP The local finite LMC model named by the prepared envelope stars is not present.
+ ---
+ duration_ms: 0.029959
+ type: 'test'
+ ...
+# Subtest: every finite star layer places its catalogue on a model whose image sits at its registered sky scale
+ok 523 - every finite star layer places its catalogue on a model whose image sits at its registered sky scale # SKIP The local finite LMC model named by the prepared envelope stars is not present.
+ ---
+ duration_ms: 0.074958
+ type: 'test'
+ ...
+# Subtest: all 943 prepared stars come from pinned measured V rows and preserve independent sky rays and native footprint
+not ok 524 - all 943 prepared stars come from pinned measured V rows and preserve independent sky rays and native footprint
+ ---
+ duration_ms: 8.79775
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4766:1'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, open 'labs/nebula/models/lmc/clouds-observation/source/target.png'"
+ code: 'ENOENT'
+ stack: |-
+ async open (node:internal/fs/promises:639:25)
+ async readFile (node:internal/fs/promises:1252:14)
+ async pinned (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4570:17)
+ async file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4587:111
+ async Promise.all (index 7)
+ async loadStarCloudModel (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4587:38)
+ ...
+# Subtest: joint CDF preserves solid-angle weighting and rejects missing cloud/density and true support gaps
+not ok 525 - joint CDF preserves solid-angle weighting and rejects missing cloud/density and true support gaps
+ ---
+ duration_ms: 0.670416
+ type: 'test'
+ location: '/private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4830:1'
+ failureType: 'testCodeFailure'
+ error: "ENOENT: no such file or directory, open 'labs/nebula/models/lmc/clouds-observation/source/target.png'"
+ code: 'ENOENT'
+ stack: |-
+ async open (node:internal/fs/promises:639:25)
+ async readFile (node:internal/fs/promises:1252:14)
+ async pinned (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4570:17)
+ async file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4587:111
+ async Promise.all (index 7)
+ async loadStarCloudModel (file:///private/tmp/cssearth-pr390/.local/nebula-lab/compiled/labs/nebula/packages/lab/src/server/workflows/stars/lmc-stars.test.mjs:4587:38)
+ ...
+# (node:87934) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 3)
+# (Use `node --trace-warnings ...` to show where the warning was created)
+# Subtest: parser rejects mismatched frames, nonfinite geometry, duplicate identifiers and invalid display values
+ok 526 - parser rejects mismatched frames, nonfinite geometry, duplicate identifiers and invalid display values
+ ---
+ duration_ms: 16.787041
+ type: 'test'
+ ...
+# Subtest: published magnitude drives a visible diameter and light hierarchy in the prepared catalogue
+ok 527 - published magnitude drives a visible diameter and light hierarchy in the prepared catalogue
+ ---
+ duration_ms: 6.066291
+ type: 'test'
+ ...
+# Subtest: actual catalogue projects through shared camera as retained CSS points, with no second east-left reflection
+ok 528 - actual catalogue projects through shared camera as retained CSS points, with no second east-left reflection
+ ---
+ duration_ms: 16.621417
+ type: 'test'
+ ...
+# Subtest: prepared stars land exactly where the prepared volume camera puts the same XYZ, at every orbit pose
+ok 529 - prepared stars land exactly where the prepared volume camera puts the same XYZ, at every orbit pose
+ ---
+ duration_ms: 33.409042
+ type: 'test'
+ ...
+# Subtest: actual prepared cloud signal and part membership govern retained star support
+ok 530 - actual prepared cloud signal and part membership govern retained star support
+ ---
+ duration_ms: 19.15675
+ type: 'test'
+ ...
+# Error: A resource generated asynchronous activity after the test ended. This activity created the error "Error: ENOENT: no such file or directory, open 'labs/nebula/models/lmc/clouds-observation/source/target.png'" which triggered an unhandledRejection event, caught by the test runner.
+# Subtest: CIC volume retains bounded mass, varies on every axis and applies one fixed color projection
+ok 531 - CIC volume retains bounded mass, varies on every axis and applies one fixed color projection
+ ---
+ duration_ms: 15.251875
+ type: 'test'
+ ...
+# Subtest: photo-constrained conversion writes source brightness independent of column mass
+ok 532 - photo-constrained conversion writes source brightness independent of column mass
+ ---
+ duration_ms: 5.211625
+ type: 'test'
+ ...
+# Subtest: pinned Bonanos (2010) rows replay deterministically into the prepared SMC stars and the model frame
+ok 533 - pinned Bonanos (2010) rows replay deterministically into the prepared SMC stars and the model frame # SKIP The local finite SMC model named by the prepared stars is not present.
+ ---
+ duration_ms: 0.337042
+ type: 'test'
+ ...
+# Subtest: footprint selection is finite V <= 16 on covered fit pixels, with nothing silently dropped
+ok 534 - footprint selection is finite V <= 16 on covered fit pixels, with nothing silently dropped # SKIP The local finite SMC model named by the prepared stars is not present.
+ ---
+ duration_ms: 0.0535
+ type: 'test'
+ ...
+# Subtest: every prepared star has positive joint emission and density support and model cutoff membership
+ok 535 - every prepared star has positive joint emission and density support and model cutoff membership # SKIP The local finite SMC model named by the prepared stars is not present.
+ ---
+ duration_ms: 0.038917
+ type: 'test'
+ ...
+# Subtest: the joint CDF guards reject rays without emission or density and never bridge a zero-support gap
+ok 536 - the joint CDF guards reject rays without emission or density and never bridge a zero-support gap # SKIP The local finite SMC model named by the prepared stars is not present.
+ ---
+ duration_ms: 0.033583
+ type: 'test'
+ ...
+# Subtest: bright catalogue stars coincide with point sources in the registered original of the model and of every lens
+ok 537 - bright catalogue stars coincide with point sources in the registered original of the model and of every lens # SKIP The local finite SMC model named by the prepared stars is not present.
+ ---
+ duration_ms: 0.0335
+ type: 'test'
+ ...
+# Subtest: each prepared layer belongs to its own model and uses the depth density that model's envelope pins
+ok 538 - each prepared layer belongs to its own model and uses the depth density that model's envelope pins # SKIP The local finite SMC model named by the prepared stars is not present.
+ ---
+ duration_ms: 0.027792
+ type: 'test'
+ ...
+# Subtest: five magnitudes carry 100 times less point light without an opacity floor
+ok 539 - five magnitudes carry 100 times less point light without an opacity floor
+ ---
+ duration_ms: 0.582625
+ type: 'test'
+ ...
+# Subtest: the same magnitude carries the same light across display colors within the available range
+ok 540 - the same magnitude carries the same light across display colors within the available range
+ ---
+ duration_ms: 0.233875
+ type: 'test'
+ ...
+# Subtest: invalid inputs reject and out-of-range bright points stay bounded
+ok 541 - invalid inputs reject and out-of-range bright points stay bounded
+ ---
+ duration_ms: 0.424792
+ type: 'test'
+ ...
+# Subtest: extracts exact xyz/mass after gas and dark records (LE)
+ok 542 - extracts exact xyz/mass after gas and dark records (LE)
+ ---
+ duration_ms: 7.839792
+ type: 'test'
+ ...
+# Subtest: extracts exact xyz/mass after gas and dark records (BE)
+ok 543 - extracts exact xyz/mass after gas and dark records (BE)
+ ---
+ duration_ms: 3.351292
+ type: 'test'
+ ...
+# Subtest: median centering reports the exact offset and preserves source order/mass
+ok 544 - median centering reports the exact offset and preserves source order/mass
+ ---
+ duration_ms: 3.791167
+ type: 'test'
+ ...
+# Subtest: header rejects unsupported precision, dimensions, extended counts and corrupt lengths
+ok 545 - header rejects unsupported precision, dimensions, extended counts and corrupt lengths
+ ---
+ duration_ms: 0.679375
+ type: 'test'
+ ...
+# Subtest: invalid ranges, source pins and selected positions fail without replacing prior output
+ok 546 - invalid ranges, source pins and selected positions fail without replacing prior output
+ ---
+ duration_ms: 5.343917
+ type: 'test'
+ ...
+# Subtest: density reconstruction follows capabilities and the exact model owner
+ok 547 - density reconstruction follows capabilities and the exact model owner
+ ---
+ duration_ms: 0.294541
+ type: 'test'
+ ...
+# Subtest: workflow state selects alignment controls, reconstruction controls and observation workspaces without DOM reads
+ok 548 - workflow state selects alignment controls, reconstruction controls and observation workspaces without DOM reads
+ ---
+ duration_ms: 0.37825
+ type: 'test'
+ ...
+# Subtest: standalone workspaces remain selectable when a historical base is absent
+ok 549 - standalone workspaces remain selectable when a historical base is absent
+ ---
+ duration_ms: 0.533708
+ type: 'test'
+ ...
+# Subtest: all camera capabilities use the same ordered controls and Model owner without pose presets
+ok 550 - all camera capabilities use the same ordered controls and Model owner without pose presets
+ ---
+ duration_ms: 7.493083
+ type: 'test'
+ ...
+# Subtest: fixed models explain missing capabilities while preserving supplied model controls
+ok 551 - fixed models explain missing capabilities while preserving supplied model controls
+ ---
+ duration_ms: 1.452458
+ type: 'test'
+ ...
+# Subtest: legacy and method-specific panels all use the shared camera and Model owner
+ok 552 - legacy and method-specific panels all use the shared camera and Model owner
+ ---
+ duration_ms: 8.335667
+ type: 'test'
+ ...
+# Subtest: only the application entrypoint may create a React root
+ok 553 - only the application entrypoint may create a React root
+ ---
+ duration_ms: 178.401542
+ type: 'test'
+ ...
+# Subtest: control portals inherit app context, preserve component state and detach through the same root
+ok 554 - control portals inherit app context, preserve component state and detach through the same root
+ ---
+ duration_ms: 332.654833
+ type: 'test'
+ ...
+# Subtest: shared appearance preserves selection callbacks and guards unsupported actions without extra controls
+ok 555 - shared appearance preserves selection callbacks and guards unsupported actions without extra controls
+ ---
+ duration_ms: 360.406417
+ type: 'test'
+ ...
+# Subtest: five-point fit recovers an offset rotated ellipse, rejects a line
+ok 556 - five-point fit recovers an offset rotated ellipse, rejects a line
+ ---
+ duration_ms: 1.461583
+ type: 'test'
+ ...
+# Subtest: automatic detection recovers an interrupted offset ellipse amid stars without a supplied center or radius
+ok 557 - automatic detection recovers an interrupted offset ellipse amid stars without a supplied center or radius
+ ---
+ duration_ms: 735.446792
+ type: 'test'
+ ...
+# Subtest: empty image and isolated stars do not manufacture nebular ellipse detections
+ok 558 - empty image and isolated stars do not manufacture nebular ellipse detections
+ ---
+ duration_ms: 49.266667
+ type: 'test'
+ ...
+# Subtest: nested ellipses are detected and grouped from pixels rather than supplied geometry
+ok 559 - nested ellipses are detected and grouped from pixels rather than supplied geometry
+ ---
+ duration_ms: 632.894334
+ type: 'test'
+ ...
+# Subtest: detected shared centers group nested projections without forcing equal orientations
+ok 560 - detected shared centers group nested projections without forcing equal orientations
+ ---
+ duration_ms: 0.204083
+ type: 'test'
+ ...
+# Subtest: geometry boundary rejects mismatched input and unbounded work settings
+ok 561 - geometry boundary rejects mismatched input and unbounded work settings
+ ---
+ duration_ms: 0.150708
+ type: 'test'
+ ...
+# Subtest: sensitivity reveals faint contours while defaults and explicit 100 percent are identical
+ok 562 - sensitivity reveals faint contours while defaults and explicit 100 percent are identical
+ ---
+ duration_ms: 252.260542
+ type: 'test'
+ ...
+# Subtest: minimum size and maximum shapes control actual proposals; settings reject invalid input
+ok 563 - minimum size and maximum shapes control actual proposals; settings reject invalid input
+ ---
+ duration_ms: 643.319042
+ type: 'test'
+ ...
+# Subtest: crossing ridges split into four supported branches and retain independent source tangents
+ok 564 - crossing ridges split into four supported branches and retain independent source tangents
+ ---
+ duration_ms: 11.296375
+ type: 'test'
+ ...
+# Subtest: unsupported gaps and no-data strips remain disconnected, including misleading unmasked scores
+ok 565 - unsupported gaps and no-data strips remain disconnected, including misleading unmasked scores
+ ---
+ duration_ms: 2.865375
+ type: 'test'
+ ...
+# Subtest: single-band ridge remains while unavailable versus eligible zero support stays distinct
+ok 566 - single-band ridge remains while unavailable versus eligible zero support stays distinct
+ ---
+ duration_ms: 2.470042
+ type: 'test'
+ ...
+# Subtest: thick supports become connected pixel-center traces and closed rings retain their loops
+ok 567 - thick supports become connected pixel-center traces and closed rings retain their loops
+ ---
+ duration_ms: 7.73825
+ type: 'test'
+ ...
+# Subtest: angular component pruning keeps short crossing branches and never creates polyline shortcuts
+ok 568 - angular component pruning keeps short crossing branches and never creates polyline shortcuts
+ ---
+ duration_ms: 3.549292
+ type: 'test'
+ ...
+# Subtest: graph rejects non-ridge combinations and invalid controls
+ok 569 - graph rejects non-ridge combinations and invalid controls
+ ---
+ duration_ms: 5.290375
+ type: 'test'
+ ...
+# Subtest: full-frame structure partition preserves faint edge emission in every RGB band
+ok 570 - full-frame structure partition preserves faint edge emission in every RGB band
+ ---
+ duration_ms: 15.538375
+ type: 'test'
+ ...
+# Subtest: directional evidence distinguishes a curved ridge from a round knot
+ok 571 - directional evidence distinguishes a curved ridge from a round knot
+ ---
+ duration_ms: 1.229791
+ type: 'test'
+ ...
+# Subtest: uncertain detail stays in the unassigned map and does not vanish
+ok 572 - uncertain detail stays in the unassigned map and does not vanish
+ ---
+ duration_ms: 1.606708
+ type: 'test'
+ ...
+# Subtest: starlet analysis and conservative assignments reconstruct nonnegative luminance
+ok 573 - starlet analysis and conservative assignments reconstruct nonnegative luminance
+ ---
+ duration_ms: 5.188625
+ type: 'test'
+ ...
+# Subtest: automatic regions distinguish a compact knot and an elongated filament deterministically
+ok 574 - automatic regions distinguish a compact knot and an elongated filament deterministically
+ ---
+ duration_ms: 6.661083
+ type: 'test'
+ ...
+# Subtest: reflection keeps boundary structure local and translated features retain coordinates
+ok 575 - reflection keeps boundary structure local and translated features retain coordinates
+ ---
+ duration_ms: 1.891083
+ type: 'test'
+ ...
+# Subtest: parent links require actual support overlap rather than bounding-box containment
+ok 576 - parent links require actual support overlap rather than bounding-box containment
+ ---
+ duration_ms: 0.11425
+ type: 'test'
+ ...
+# Subtest: deprojection integrates back to the published projected ellipse at different inclinations
+ok 577 - deprojection integrates back to the published projected ellipse at different inclinations
+ ---
+ duration_ms: 6.250792
+ type: 'test'
+ ...
+# Subtest: line-of-sight mirror is an explicit degeneracy and source pins participate in identity
+ok 578 - line-of-sight mirror is an explicit degeneracy and source pins participate in identity
+ ---
+ duration_ms: 0.32475
+ type: 'test'
+ ...
+# Subtest: conditional finite features fill the prior depth distribution instead of collapsing into its modal plane
+ok 579 - conditional finite features fill the prior depth distribution instead of collapsing into its modal plane
+ ---
+ duration_ms: 793.022833
+ type: 'test'
+ ...
+# Subtest: conditional draws reject unsupported light and thick boundary kernels instead of making midplane clumps
+ok 580 - conditional draws reject unsupported light and thick boundary kernels instead of making midplane clumps
+ ---
+ duration_ms: 0.434584
+ type: 'test'
+ ...
+# Subtest: envelope depth follows the simulation, never the image
+ok 581 - envelope depth follows the simulation, never the image
+ ---
+ duration_ms: 27.399584
+ type: 'test'
+ ...
+# Subtest: envelope projection matches the smoothed image scaled by fraction and floor adds only where the simulation exists
+ok 582 - envelope projection matches the smoothed image scaled by fraction and floor adds only where the simulation exists
+ ---
+ duration_ms: 24.793958
+ type: 'test'
+ ...
+# Subtest: chromaticity is peak-normalized, smoothed and neutral outside coverage
+ok 583 - chromaticity is peak-normalized, smoothed and neutral outside coverage
+ ---
+ duration_ms: 4.335875
+ type: 'test'
+ ...
+# Subtest: the chroma trust ramp is an authored envelope setting whose default reproduces the accepted bytes
+ok 584 - the chroma trust ramp is an authored envelope setting whose default reproduces the accepted bytes
+ ---
+ duration_ms: 3.52075
+ type: 'test'
+ ...
+# Subtest: an authored sky quantile and footprint-edge taper fix what the trust ramp alone could only trade off
+ok 585 - an authored sky quantile and footprint-edge taper fix what the trust ramp alone could only trade off
+ ---
+ duration_ms: 5.211792
+ type: 'test'
+ ...
+# Subtest: prior modes condition finite depth without changing fitted projected light or source input
+ok 586 - prior modes condition finite depth without changing fitted projected light or source input
+ ---
+ duration_ms: 3.748917
+ type: 'test'
+ ...
+# Subtest: unsupported image structures remain finite and explicit rather than silently discarded
+ok 587 - unsupported image structures remain finite and explicit rather than silently discarded
+ ---
+ duration_ms: 1.873208
+ type: 'test'
+ ...
+# Subtest: adapter projection agrees with conditioned field; default image-only fitting remains unchanged
+ok 588 - adapter projection agrees with conditioned field; default image-only fitting remains unchanged
+ ---
+ duration_ms: 24.496084
+ type: 'test'
+ ...
+# Subtest: compact XYZ regions preserve partition and distinguish overlapping near/far material; XY extrusion fails
+ok 589 - compact XYZ regions preserve partition and distinguish overlapping near/far material; XY extrusion fails
+ ---
+ duration_ms: 11.540917
+ type: 'test'
+ ...
+# Subtest: fixed overlapping depth-transmission columns recover RGB and improve independent withheld pixels
+ok 590 - fixed overlapping depth-transmission columns recover RGB and improve independent withheld pixels
+ ---
+ duration_ms: 26.075375
+ type: 'test'
+ ...
+# Subtest: holdout pixels never enter gradients and repeated columns retain their unresolved ambiguity
+ok 591 - holdout pixels never enter gradients and repeated columns retain their unresolved ambiguity
+ ---
+ duration_ms: 10.971208
+ type: 'test'
+ ...
+# Subtest: solver validates shapes, positive operator, bounded options, RGB ranges and training/validation coverage
+ok 592 - solver validates shapes, positive operator, bounded options, RGB ranges and training/validation coverage
+ ---
+ duration_ms: 2.806333
+ type: 'test'
+ ...
+# Subtest: emission processing preserves channel order, black subtraction and voxel-centre normalization
+ok 593 - emission processing preserves channel order, black subtraction and voxel-centre normalization
+ ---
+ duration_ms: 0.751958
+ type: 'test'
+ ...
+# Subtest: a ring prior retains its empty center and never paints unsupported image pixels
+ok 594 - a ring prior retains its empty center and never paints unsupported image pixels
+ ---
+ duration_ms: 6.091041
+ type: 'test'
+ ...
+# Subtest: changing the image changes emission but not the supplied depth distribution
+ok 595 - changing the image changes emission but not the supplied depth distribution
+ ---
+ duration_ms: 1.430209
+ type: 'test'
+ ...
+# Subtest: positive group prox clips peaks together, rather than averaging or independent soft thresholding
+ok 596 - positive group prox clips peaks together, rather than averaging or independent soft thresholding
+ ---
+ duration_ms: 0.5755
+ type: 'test'
+ ...
+# Subtest: all voxels belong to exactly one cylindrical group
+ok 597 - all voxels belong to exactly one cylindrical group
+ ---
+ duration_ms: 0.655834
+ type: 'test'
+ ...
+# Subtest: a projected hollow cylindrical shell recovers radial depth instead of a photograph extrusion
+ok 598 - a projected hollow cylindrical shell recovers radial depth instead of a photograph extrusion
+ ---
+ duration_ms: 27.291875
+ type: 'test'
+ ...
+# Subtest: compact matching preserves reciprocal constellation identities under frame translation
+ok 599 - compact matching preserves reciprocal constellation identities under frame translation
+ ---
+ duration_ms: 0.949542
+ type: 'test'
+ ...
+# Subtest: compact detector rejects grid mismatch and does not manufacture stars on a blank image
+ok 600 - compact detector rejects grid mismatch and does not manufacture stars on a blank image
+ ---
+ duration_ms: 9.889833
+ type: 'test'
+ ...
+# Subtest: model-only similarity recovers asymmetric structure without changing particles or target
+ok 601 - model-only similarity recovers asymmetric structure without changing particles or target
+ ---
+ duration_ms: 25.647416
+ type: 'test'
+ ...
+# Subtest: Fixed catalogue TAN inverts native publisher pixel rays
+ok 602 - Fixed catalogue TAN inverts native publisher pixel rays
+ ---
+ duration_ms: 0.679875
+ type: 'test'
+ ...
+# Subtest: Fixed catalogue SIN inverts native publisher pixel rays
+ok 603 - Fixed catalogue SIN inverts native publisher pixel rays
+ ---
+ duration_ms: 0.09525
+ type: 'test'
+ ...
+# Subtest: the chance control scales with detection density and still refuses every known-bad fixed WCS
+ok 604 - the chance control scales with detection density and still refuses every known-bad fixed WCS
+ ---
+ duration_ms: 0.214375
+ type: 'test'
+ ...
+# Subtest: physical transform order, independent axis scales and perspective use consistent dimensions
+ok 605 - physical transform order, independent axis scales and perspective use consistent dimensions
+ ---
+ duration_ms: 0.932625
+ type: 'test'
+ ...
+# Subtest: magnitude uncertainty broadens observable histogram without modifying latent particles
+ok 606 - magnitude uncertainty broadens observable histogram without modifying latent particles
+ ---
+ duration_ms: 4.668459
+ type: 'test'
+ ...
+# Subtest: uncertainty scatters sources just outside magnitude selection into observed bins
+ok 607 - uncertainty scatters sources just outside magnitude selection into observed bins
+ ---
+ duration_ms: 0.694292
+ type: 'test'
+ ...
+# Subtest: heldout counts never select parameters, amplitude or starts; empty observed cells contribute
+ok 608 - heldout counts never select parameters, amplitude or starts; empty observed cells contribute
+ ---
+ duration_ms: 17.335625
+ type: 'test'
+ ...
+# Subtest: missing footprint cells are excluded, and invalid dimensions, scales or budgets fail
+ok 609 - missing footprint cells are excluded, and invalid dimensions, scales or budgets fail
+ ---
+ duration_ms: 1.43825
+ type: 'test'
+ ...
+# Subtest: explicit reference and negative reddening floor preserve published calculation
+ok 610 - explicit reference and negative reddening floor preserve published calculation
+ ---
+ duration_ms: 0.449833
+ type: 'test'
+ ...
+# Subtest: calibration is required, validated and controls the reference distance
+ok 611 - calibration is required, validated and controls the reference distance
+ ---
+ duration_ms: 0.126458
+ type: 'test'
+ ...
+# Subtest: reject invalid photometry and ambiguous/outside matches
+ok 612 - reject invalid photometry and ambiguous/outside matches
+ ---
+ duration_ms: 0.11325
+ type: 'test'
+ ...
+# Subtest: smooth basis is an additive partition and cached projections reproduce weighted projection
+ok 613 - smooth basis is an additive partition and cached projections reproduce weighted projection
+ ---
+ duration_ms: 6.388459
+ type: 'test'
+ ...
+# Subtest: bounded weights fit training only, with geometry frozen and explicit prior
+ok 614 - bounded weights fit training only, with geometry frozen and explicit prior
+ ---
+ duration_ms: 18.509792
+ type: 'test'
+ ...
+# Subtest: unsupported octants retain unit weights; invalid options and histograms fail
+ok 615 - unsupported octants retain unit weights; invalid options and histograms fail
+ ---
+ duration_ms: 0.889625
+ type: 'test'
+ ...
+# Subtest: joint depth keeps its fixed identifier quantile in a uniform neutral source
+ok 616 - joint depth keeps its fixed identifier quantile in a uniform neutral source
+ ---
+ duration_ms: 2.369041
+ type: 'test'
+ ...
+# Subtest: archive acquisition uses injected endpoint, angular window and transport without a catalogue owner
+ok 617 - archive acquisition uses injected endpoint, angular window and transport without a catalogue owner
+ ---
+ duration_ms: 11.673042
+ type: 'test'
+ ...
+# Subtest: a marginal archive overlap cannot be promoted as a wide-field M31 image
+ok 618 - a marginal archive overlap cannot be promoted as a wide-field M31 image
+ ---
+ duration_ms: 1.288333
+ type: 'test'
+ ...
+# Subtest: rotated and RA-wrapped footprints retain partial versus complete reference extent
+ok 619 - rotated and RA-wrapped footprints retain partial versus complete reference extent
+ ---
+ duration_ms: 0.420833
+ type: 'test'
+ ...
+# Subtest: the reported ESO e0941 field also misses M24 despite its large FOV
+ok 620 - the reported ESO e0941 field also misses M24 despite its large FOV
+ ---
+ duration_ms: 0.08625
+ type: 'test'
+ ...
+# Subtest: cached FITS Source links view the exact file instead of opening the download endpoint
+ok 621 - cached FITS Source links view the exact file instead of opening the download endpoint
+ ---
+ duration_ms: 0.600542
+ type: 'test'
+ ...
+# Subtest: real source pages remain source pages; direct preview and FITS download remain independent
+ok 622 - real source pages remain source pages; direct preview and FITS download remain independent
+ ---
+ duration_ms: 0.134667
+ type: 'test'
+ ...
+# Subtest: DataLink metadata opens as browser records and cannot masquerade as an image or FITS download
+ok 623 - DataLink metadata opens as browser records and cannot masquerade as an image or FITS download
+ ---
+ duration_ms: 0.179709
+ type: 'test'
+ ...
+# Subtest: a detailed wide mosaic beats a sharper tiny crop and an unreported frame across archives
+ok 624 - a detailed wide mosaic beats a sharper tiny crop and an unreported frame across archives
+ ---
+ duration_ms: 1.022
+ type: 'test'
+ ...
+# Subtest: mask products and clearly disjoint fields cannot win with their size or sharpness
+ok 625 - mask products and clearly disjoint fields cannot win with their size or sharpness
+ ---
+ duration_ms: 0.168375
+ type: 'test'
+ ...
+# Subtest: field size and pixel sampling cap detail; missing metadata remains unreported
+ok 626 - field size and pixel sampling cap detail; missing metadata remains unreported
+ ---
+ duration_ms: 0.077084
+ type: 'test'
+ ...
+# Subtest: native image helpers preserve source pixels and positive accounting without a model
+ok 627 - native image helpers preserve source pixels and positive accounting without a model
+ ---
+ duration_ms: 6.194917
+ type: 'test'
+ ...
+# Subtest: configured worker gets its exact request and reports process failure without running NOX
+ok 628 - configured worker gets its exact request and reports process failure without running NOX
+ ---
+ duration_ms: 94.793667
+ type: 'test'
+ ...
+# Subtest: density windows preserve little-endian cells and translate only their coordinate bounds
+ok 629 - density windows preserve little-endian cells and translate only their coordinate bounds
+ ---
+ duration_ms: 1.453042
+ type: 'test'
+ ...
+# Subtest: the actual compiler default creates a bounded plan without an opt-in sampling override
+ok 630 - the actual compiler default creates a bounded plan without an opt-in sampling override
+ ---
+ duration_ms: 965.068958
+ type: 'test'
+ ...
+# Subtest: actual accepted M42 sampling bypasses optimization with its historical omission unchanged
+ok 631 - actual accepted M42 sampling bypasses optimization with its historical omission unchanged
+ ---
+ duration_ms: 13.42875
+ type: 'test'
+ ...
+# Subtest: saved planned sampling is replayed exactly and rejects a mismatching retained count
+ok 632 - saved planned sampling is replayed exactly and rejects a mismatching retained count
+ ---
+ duration_ms: 0.3305
+ type: 'test'
+ ...
+# Subtest: new authoring reserves stars before probing and cannot use explicit old sampling as a budget bypass
+ok 633 - new authoring reserves stars before probing and cannot use explicit old sampling as a budget bypass
+ ---
+ duration_ms: 0.130125
+ type: 'test'
+ ...
+# Subtest: the optimizer receives the remaining budget after retained stars, not a declaration-only 500 limit
+ok 634 - the optimizer receives the remaining budget after retained stars, not a declaration-only 500 limit
+ ---
+ duration_ms: 930.708625
+ type: 'test'
+ ...
+# Subtest: a small historical compiler replay keeps unreflected source geometry, sampling and physical intervals absent
+ok 635 - a small historical compiler replay keeps unreflected source geometry, sampling and physical intervals absent
+ ---
+ duration_ms: 415.092291
+ type: 'test'
+ ...
+# Subtest: omitted layer layout preserves the original uniform manifest and PNG bytes
+ok 636 - omitted layer layout preserves the original uniform manifest and PNG bytes
+ ---
+ duration_ms: 34.194875
+ type: 'test'
+ ...
+# Subtest: nonuniform intervals retain every reference midpoint and paint the actual integrated color
+ok 637 - nonuniform intervals retain every reference midpoint and paint the actual integrated color
+ ---
+ duration_ms: 37.772875
+ type: 'test'
+ ...
+# Subtest: layer plans reject gaps, overlaps, nonfinite values, excess layers and excessive sample counts
+ok 638 - layer plans reject gaps, overlaps, nonfinite values, excess layers and excessive sample counts
+ ---
+ duration_ms: 1.678334
+ type: 'test'
+ ...
+# Subtest: all retained stars, optical copies and wrappers consume the same element budget
+ok 639 - all retained stars, optical copies and wrappers consume the same element budget
+ ---
+ duration_ms: 0.564333
+ type: 'test'
+ ...
+# Subtest: a saved budget rejects changed geometry, hidden-point omissions and forged counters
+ok 640 - a saved budget rejects changed geometry, hidden-point omissions and forged counters
+ ---
+ duration_ms: 0.400125
+ type: 'test'
+ ...
+# Subtest: measured distance and sky direction determine physical positions without rescaling
+ok 641 - measured distance and sky direction determine physical positions without rescaling
+ ---
+ duration_ms: 0.815625
+ type: 'test'
+ ...
+# Subtest: inverse frame orientation preserves distance and handedness
+ok 642 - inverse frame orientation preserves distance and handedness
+ ---
+ duration_ms: 0.085833
+ type: 'test'
+ ...
+# Subtest: rejects missing distances, invalid sky coordinates and non-unit frames
+ok 643 - rejects missing distances, invalid sky coordinates and non-unit frames
+ ---
+ duration_ms: 0.135125
+ type: 'test'
+ ...
+# Subtest: observed angular scale follows distance, not the chosen image dimensions
+ok 644 - observed angular scale follows distance, not the chosen image dimensions
+ ---
+ duration_ms: 0.067625
+ type: 'test'
+ ...
+# Subtest: prepared reflection keeps planned intervals, sample counts and logical indices valid in the reflected frame
+ok 645 - prepared reflection keeps planned intervals, sample counts and logical indices valid in the reflected frame
+ ---
+ duration_ms: 1.844458
+ type: 'test'
+ ...
+# Subtest: model placement transports full support with invertible points and rotated normals
+ok 646 - model placement transports full support with invertible points and rotated normals
+ ---
+ duration_ms: 0.954708
+ type: 'test'
+ ...
+# Subtest: authored placement changes coordinates only, retaining every texture pin, pixel grid, UV and alpha statistic
+ok 647 - authored placement changes coordinates only, retaining every texture pin, pixel grid, UV and alpha statistic
+ ---
+ duration_ms: 0.241917
+ type: 'test'
+ ...
+# Subtest: one Earth ray retains its image coordinate at different physical depths
+ok 648 - one Earth ray retains its image coordinate at different physical depths
+ ---
+ duration_ms: 0.417583
+ type: 'test'
+ ...
+# Subtest: physical bake bounds retain every finite field corner and interior sample
+ok 649 - physical bake bounds retain every finite field corner and interior sample
+ ---
+ duration_ms: 0.681958
+ type: 'test'
+ ...
+# Subtest: emission per angular depth converts to emission per kpc without changing projected light
+ok 650 - emission per angular depth converts to emission per kpc without changing projected light
+ ---
+ duration_ms: 1.954667
+ type: 'test'
+ ...
+# Subtest: invalid observer, singular rays and reversed bounds are rejected
+ok 651 - invalid observer, singular rays and reversed bounds are rejected
+ ---
+ duration_ms: 0.224208
+ type: 'test'
+ ...
+# Subtest: one affine alignment maps the same sky point identically around different image pivots
+ok 652 - one affine alignment maps the same sky point identically around different image pivots
+ ---
+ duration_ms: 0.679333
+ type: 'test'
+ ...
+# Subtest: alignment rejects a placement missing any affine property
+ok 653 - alignment rejects a placement missing any affine property
+ ---
+ duration_ms: 0.249584
+ type: 'test'
+ ...
+# Subtest: manual placement rejects invalid controls without changing accepted state
+ok 654 - manual placement rejects invalid controls without changing accepted state
+ ---
+ duration_ms: 0.918959
+ type: 'test'
+ ...
+# Subtest: reset restores the exact prepared projective transform
+ok 655 - reset restores the exact prepared projective transform
+ ---
+ duration_ms: 0.16825
+ type: 'test'
+ ...
+# Subtest: identity registration preserves the publisher WCS footprint
+ok 656 - identity registration preserves the publisher WCS footprint
+ ---
+ duration_ms: 0.720916
+ type: 'test'
+ ...
+# Subtest: a nontrivial projective registration maps all four image corners through H
+ok 657 - a nontrivial projective registration maps all four image corners through H
+ ---
+ duration_ms: 0.2125
+ type: 'test'
+ ...
+# Subtest: rotated negative-coordinate footprint accepts either winding and tapers inward only
+ok 658 - rotated negative-coordinate footprint accepts either winding and tapers inward only
+ ---
+ duration_ms: 0.818208
+ type: 'test'
+ ...
+# Subtest: external window decoder rejects nonfinite, concave, crossing, degenerate and unknown data
+ok 659 - external window decoder rejects nonfinite, concave, crossing, degenerate and unknown data
+ ---
+ duration_ms: 0.495167
+ type: 'test'
+ ...
+# Subtest: window acts on the actual XYZ field, preserves interior material, and limits integrated projection
+ok 660 - window acts on the actual XYZ field, preserves interior material, and limits integrated projection
+ ---
+ duration_ms: 17.670834
+ type: 'test'
+ ...
+# Subtest: absent window preserves exact historical emission and retained model validation keeps the selection
+ok 661 - absent window preserves exact historical emission and retained model validation keeps the selection
+ ---
+ duration_ms: 2.921333
+ type: 'test'
+ ...
+# Subtest: tilted finite patches retain their analytic z-integrated light at independent rays
+ok 662 - tilted finite patches retain their analytic z-integrated light at independent rays
+ ---
+ duration_ms: 14.933459
+ type: 'test'
+ ...
+# Subtest: sheared support bounds and spatial buckets contain every transformed interior sample
+ok 663 - sheared support bounds and spatial buckets contain every transformed interior sample
+ ---
+ duration_ms: 2.0515
+ type: 'test'
+ ...
+# Subtest: a side projection follows a continuous tilted patch instead of unchanged vertical columns
+ok 664 - a side projection follows a continuous tilted patch instead of unchanged vertical columns
+ ---
+ duration_ms: 12.062917
+ type: 'test'
+ ...
+# Subtest: historical zero gradients preserve values while malformed and unbounded gradients are rejected
+ok 665 - historical zero gradients preserve values while malformed and unbounded gradients are rejected
+ ---
+ duration_ms: 1.683375
+ type: 'test'
+ ...
+# Subtest: finite component material retains distinct depth colors where sky footprints overlap; XY extrusion fails
+ok 666 - finite component material retains distinct depth colors where sky footprints overlap; XY extrusion fails
+ ---
+ duration_ms: 3.716666
+ type: 'test'
+ ...
+# Subtest: indexed material shares exact neutral weights and reports incomplete/black coverage
+ok 667 - indexed material shares exact neutral weights and reports incomplete/black coverage
+ ---
+ duration_ms: 3.291584
+ type: 'test'
+ ...
+# Subtest: colors stay attached to existing emitters when depth changes; coincident projections remain explicitly ambiguous
+ok 668 - colors stay attached to existing emitters when depth changes; coincident projections remain explicitly ambiguous
+ ---
+ duration_ms: 3.021458
+ type: 'test'
+ ...
+# Subtest: explicit slab offsets and quadrature spacing govern chroma and alpha-limited color
+ok 669 - explicit slab offsets and quadrature spacing govern chroma and alpha-limited color
+ ---
+ duration_ms: 1.476917
+ type: 'test'
+ ...
+# Subtest: thin displaced emission takes its own image color rather than the empty slice center
+ok 670 - thin displaced emission takes its own image color rather than the empty slice center
+ ---
+ duration_ms: 0.184792
+ type: 'test'
+ ...
+# Subtest: constant source color stays constant through every bank and missing coverage stays explicit
+ok 671 - constant source color stays constant through every bank and missing coverage stays explicit
+ ---
+ duration_ms: 0.645667
+ type: 'test'
+ ...
+# Subtest: weighted full-intensity channels remain inside the RGB transport range after floating-point division
+ok 672 - weighted full-intensity channels remain inside the RGB transport range after floating-point division
+ ---
+ duration_ms: 0.486709
+ type: 'test'
+ ...
+# Subtest: XYZ slab materials sample identical emitting coordinates and preserve component mixtures
+ok 673 - XYZ slab materials sample identical emitting coordinates and preserve component mixtures
+ ---
+ duration_ms: 0.75975
+ type: 'test'
+ ...
+# Subtest: alpha-limited slab material keeps chroma in dense slabs and neutralizes thin ones
+ok 674 - alpha-limited slab material keeps chroma in dense slabs and neutralizes thin ones
+ ---
+ duration_ms: 0.15925
+ type: 'test'
+ ...
+# Subtest: a lens channel gain scales its own chromaticity and never tints the zone the alpha limit neutralized
+ok 675 - a lens channel gain scales its own chromaticity and never tints the zone the alpha limit neutralized
+ ---
+ duration_ms: 0.518667
+ type: 'test'
+ ...
+# Subtest: a lens tone curve moves the render onto its curve, stays neutral in the faint zone, and is absent-exact
+ok 676 - a lens tone curve moves the render onto its curve, stays neutral in the faint zone, and is absent-exact
+ ---
+ duration_ms: 0.443792
+ type: 'test'
+ ...
+# Subtest: default planner deterministically caps the combined XYZ layers at 500 without dropping cells
+ok 677 - default planner deterministically caps the combined XYZ layers at 500 without dropping cells
+ ---
+ duration_ms: 399.536292
+ type: 'test'
+ ...
+# Subtest: an off-center concentrated feature receives finer groups than faint outer support
+ok 678 - an off-center concentrated feature receives finer groups than faint outer support
+ ---
+ duration_ms: 118.335208
+ type: 'test'
+ ...
+# Subtest: small complete partitions and the combined budget match an independent exhaustive displacement oracle
+ok 679 - small complete partitions and the combined budget match an independent exhaustive displacement oracle
+ ---
+ duration_ms: 1.604417
+ type: 'test'
+ ...
+# Subtest: planner rejects invalid or empty emission and responds to cancellation before and during probes
+ok 680 - planner rejects invalid or empty emission and responds to cancellation before and during probes
+ ---
+ duration_ms: 7.970166
+ type: 'test'
+ ...
+# Subtest: compiler Earth fit matches Alignment full-footprint bounds and 18px inset
+ok 681 - compiler Earth fit matches Alignment full-footprint bounds and 18px inset
+ ---
+ duration_ms: 0.786
+ type: 'test'
+ ...
+# Subtest: inspection center remains the orbit pivot without changing the user pan
+ok 682 - inspection center remains the orbit pivot without changing the user pan
+ ---
+ duration_ms: 0.098292
+ type: 'test'
+ ...
+# Subtest: prepared lab stars preserve camera-relative depth, orientation and principal offset
+ok 683 - prepared lab stars preserve camera-relative depth, orientation and principal offset
+ ---
+ duration_ms: 0.703
+ type: 'test'
+ ...
+# Subtest: default uses exact reference; subsets select owned prepared leaves and empty means empty
+ok 684 - default uses exact reference; subsets select owned prepared leaves and empty means empty
+ ---
+ duration_ms: 0.983833
+ type: 'test'
+ ...
+# Subtest: catalogue rejects ambiguous, missing, duplicated or unrelated contributions
+ok 685 - catalogue rejects ambiguous, missing, duplicated or unrelated contributions
+ ---
+ duration_ms: 0.225792
+ type: 'test'
+ ...
+# Subtest: brightness uses effective source-over contributions through axis handoffs
+ok 686 - brightness uses effective source-over contributions through axis handoffs
+ ---
+ duration_ms: 0.693458
+ type: 'test'
+ ...
+1..686
+# tests 686
+# suites 0
+# pass 664
+# fail 5
+# cancelled 0
+# skipped 17
+# todo 0
+# duration_ms 14359.153166
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lens-eso0844a.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lens-eso0844a.png
new file mode 100644
index 0000000000..6ebacde32b
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lens-eso0844a.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lens-eso1119b.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lens-eso1119b.png
new file mode 100644
index 0000000000..82dd140717
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/lens-eso1119b.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/native-arrival.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/native-arrival.png
new file mode 100644
index 0000000000..a185d9dc29
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/native-arrival.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/native-focus.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/native-focus.json
new file mode 100644
index 0000000000..0a8f8d9bbc
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/native-focus.json
@@ -0,0 +1,330 @@
+{
+ "base": "http://127.0.0.1:4219",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "browser": "148.0.7778.96",
+ "inputPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 433153,
+ "sha256": "42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c"
+ }
+ ],
+ "errors": [],
+ "failed": [],
+ "retainedCounts": [
+ {
+ "stage": "cold arrival",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "warm reselection",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "orbit",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "close zoom",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "return from galaxy context",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "lens eso1119b",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "lens eso0844a",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ }
+ ],
+ "cold": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "coldContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "warm": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "dragged": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -107690829656048200000,
+ -41068870101575090000,
+ -115681989469650910000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ -4.3973168737309055e-19,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 8165459600736778000,
+ "distanceRadii": 6.096653162046747,
+ "projectedRadiusPixels": 204.55157392967632
+ },
+ "zoomed": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105161342581436300000,
+ -41827716223958670000,
+ -123337799964711390000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ -4.3973168737309055e-19,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 66966738829483176,
+ "distanceRadii": 0.049999999999986326,
+ "projectedRadiusPixels": 24941.60000000682
+ },
+ "reselected": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -107690829656048270000,
+ -41068870101575070000,
+ -115681989469650940000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ 0,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 8165459600736769000,
+ "distanceRadii": 6.0966531620467395,
+ "projectedRadiusPixels": 204.55157392967655
+ },
+ "reselectedContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "zoomedOut": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -146973272045458080000,
+ -29284137384752263000,
+ 3211258784085832700
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ 0,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 133933478928421730000,
+ "distanceRadii": 100.00000094779794,
+ "projectedRadiusPixels": 12.470799881802014
+ },
+ "farContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 0.464877
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 0.999838
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0740891
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 0.999838
+ }
+ ],
+ "overview": [
+ {
+ "display": "block",
+ "opacity": 0.534735
+ }
+ ]
+ },
+ "returnedContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ },
+ {
+ "id": "m8",
+ "display": "none",
+ "opacity": 0
+ },
+ {
+ "id": "smc",
+ "display": "none",
+ "opacity": 0
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "rendering": {
+ "totalElements": 22280,
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": "1",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "id": "m8",
+ "display": "none",
+ "opacity": "0",
+ "elements": 2291,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "none",
+ "opacity": "0",
+ "elements": 2636,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "galaxyOverview": [
+ {
+ "opacity": "1",
+ "display": "none",
+ "elements": 1382
+ }
+ ]
+ },
+ "result": "passed"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-after.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-after.png
new file mode 100644
index 0000000000..8a3c151efa
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-after.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-before.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-before.png
new file mode 100644
index 0000000000..d9208e0fff
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-before.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-diff.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-diff.png
new file mode 100644
index 0000000000..cf06a35bcd
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/neutral-front-diff.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/pixelmatch.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/pixelmatch.json
new file mode 100644
index 0000000000..b24e288395
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/pixelmatch.json
@@ -0,0 +1,84 @@
+{
+ "method": "Matched 800x700 Lab renderer front views, fixed camera and unchanged emission/materials; only sampling allocation changes. Pixelmatch0.1, antialiasing excluded. Differences document the approximation and do not establish improved visual or scientific quality.",
+ "oldScene": "7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549",
+ "newScene": "59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e",
+ "threshold": 0.1,
+ "includeAA": false,
+ "comparisons": [
+ {
+ "lens": "neutral",
+ "width": 800,
+ "height": 700,
+ "changedPixels": 1702,
+ "fraction": 0.0030392857142857144,
+ "brightnessAfterOverBefore": 1.2468683021993587,
+ "inputs": [
+ {
+ "path": "neutral-front-before.png",
+ "bytes": 202666,
+ "sha256": "f8c7f2cd102f2d7e60da2bc6164c1da62f419bca83a8c8df98820ddf939ae25e"
+ },
+ {
+ "path": "neutral-front-after.png",
+ "bytes": 214480,
+ "sha256": "7590c17ff355bf771c2a208d7d454ecbc2037cbef5289c65354ce6669810732c"
+ }
+ ],
+ "diff": {
+ "path": "neutral-front-diff.png",
+ "bytes": 83691,
+ "sha256": "072dc8f4ac0857c36396c4e1e21b05b6cefd5b868e77090adc1e878ae7938a78"
+ }
+ },
+ {
+ "lens": "eso1119b",
+ "width": 800,
+ "height": 700,
+ "changedPixels": 1695,
+ "fraction": 0.0030267857142857145,
+ "brightnessAfterOverBefore": 1.2503840846997605,
+ "inputs": [
+ {
+ "path": "eso1119b-front-before.png",
+ "bytes": 274760,
+ "sha256": "1b296b5a7a27c029f94c79bb8a907b1447e34f6c71ef94336fc9a66114630d12"
+ },
+ {
+ "path": "eso1119b-front-after.png",
+ "bytes": 287698,
+ "sha256": "270c13642ff003eedbd273d3bf7e081e0d6cefd03e26a88d899b431c214dbc9c"
+ }
+ ],
+ "diff": {
+ "path": "eso1119b-front-diff.png",
+ "bytes": 79886,
+ "sha256": "0128a11e61a7586089e8fc949de2aec6221ff6adeacc7098f7927e5088461ca8"
+ }
+ },
+ {
+ "lens": "eso0844a",
+ "width": 800,
+ "height": 700,
+ "changedPixels": 1699,
+ "fraction": 0.0030339285714285715,
+ "brightnessAfterOverBefore": 1.2345043902506054,
+ "inputs": [
+ {
+ "path": "eso0844a-front-before.png",
+ "bytes": 251489,
+ "sha256": "f6425d7082bc122195dc111cfccae07a4ed8da6f0f0575e7304e6e250da695e6"
+ },
+ {
+ "path": "eso0844a-front-after.png",
+ "bytes": 270964,
+ "sha256": "d4a6a7a4c1b0cce6121b5c03f20afb43c4f75adcf64ed86339a1a0ffe5cf5e45"
+ }
+ ],
+ "diff": {
+ "path": "eso0844a-front-diff.png",
+ "bytes": 81649,
+ "sha256": "05657ca3536050daaa7b656039f5085e5107ac8e62dcd63b49523c39b40b8989"
+ }
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/planner-mutation.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/planner-mutation.json
new file mode 100644
index 0000000000..4a8d3963a1
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/planner-mutation.json
@@ -0,0 +1,12 @@
+{
+ "mutation": "Omit maximumLayers from actual optimizeVolumeLayers call in isolated copied baker.",
+ "exitCode": 1,
+ "tests": 2,
+ "failed": 2,
+ "observedFailures": [
+ "Prepared volume needs 623 retained elements; limit is 500.",
+ "Prepared volume needs 626 retained elements; limit is 500."
+ ],
+ "interpretation": "Both real default planning tests go red. The final receipt guard rejects the oversized result before it can be returned; the live optimizer passes31targetedtests.",
+ "liveProducerChanged": false
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/r2-restore.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/r2-restore.json
new file mode 100644
index 0000000000..77d26ef672
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/r2-restore.json
@@ -0,0 +1,609 @@
+{
+ "status": "verified",
+ "inventoriesUnchangedThroughoutVerification": true,
+ "publisherVerifiedKeys": 64,
+ "claim": "Publication availability and exact pinned bytes; separate from scientific/visual acceptance and compact replay",
+ "root": "/private/tmp/cssearth-pr390/output/pr390-audit/element-budget/r2-restore/omega-r2-cold-1789945192800",
+ "startedAt": "2026-09-20T22:59:52.810Z",
+ "endedAt": "2026-09-20T22:59:54.758Z",
+ "durationSeconds": 1.948190667,
+ "node": "v22.23.2",
+ "platform": "darwin",
+ "arch": "arm64",
+ "inputPins": {
+ "object.json": "e709e55c5ec0ad4f4846303cffec0d6c34c235554a69e5a8481535e9465e4255",
+ "runtime-assets.json": "e30940cfcec240029ada2ddd666379804e57d863087d97a7ad91be598cb0f5bf",
+ "prepared-assets.json": "ac43536da6c0b558e221253a673b14c30dcecd61d5a504c38936ef307351a448"
+ },
+ "restored": {
+ "installed": 64,
+ "reused": 0,
+ "skipped": 0
+ },
+ "repeat": {
+ "installed": 0,
+ "reused": 64,
+ "skipped": 0
+ },
+ "descriptorPreparedSha256": "42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c",
+ "verified": [
+ {
+ "filename": "datasets/8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb.webp",
+ "bytes": 187756,
+ "sha256": "8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb"
+ },
+ {
+ "filename": "datasets/33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1.webp",
+ "bytes": 218816,
+ "sha256": "33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1"
+ },
+ {
+ "filename": "delivery.json",
+ "bytes": 695,
+ "sha256": "b62c0d4a8203fc424c1224ba7fb6a2c780f98abc25da2d3bd95e17d0145ec2ba"
+ },
+ {
+ "filename": "eso0844a/atlases/x.webp",
+ "bytes": 630850,
+ "sha256": "2be14d24b7b8c9207982a44d0257d2d33e9e2f4c6f1a20e11e8e83a2d419ff3e"
+ },
+ {
+ "filename": "eso0844a/atlases/y.webp",
+ "bytes": 588746,
+ "sha256": "f378d5167d5c82e2779c3507a67f5f3cc7d3cd67e8396a70a5e8d47d0bca3247"
+ },
+ {
+ "filename": "eso0844a/atlases/z.webp",
+ "bytes": 619926,
+ "sha256": "f2cfe0f147d8dd07d66be1acb6442c9898e52a37426ed332818e704111ba2a6a"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00n.png",
+ "bytes": 12813,
+ "sha256": "4156c57dd44f146b3de1b146f31da37f676ef5ca97c388fc03d3442064c3fcae"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00p.png",
+ "bytes": 12843,
+ "sha256": "7827406e40fef2a444bf1da7c92dbb73aea22132dc4da142cbddaa1c3038b954"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0n0.png",
+ "bytes": 14310,
+ "sha256": "e9d6d2605bd483e58d3ff86bb49f010a0dbb3e88bd19a7529f56623288d5bb90"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0nn.png",
+ "bytes": 16252,
+ "sha256": "26161006830d714bee7e57dc84aa2bde9a22fb2d375d32188231d5b9fe6ca1d0"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0np.png",
+ "bytes": 16701,
+ "sha256": "089d623a473e4fa400428e882ed675afdfe55fa15d05c0dc05879dc5279f234c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0p0.png",
+ "bytes": 14252,
+ "sha256": "3612a4bb86230a87d514ae5417aa57300768b1a10d61f482414dfae1d4806da9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pn.png",
+ "bytes": 16654,
+ "sha256": "c8621057e346b5167a188ece82721e947bc5d06d358edc6883ab2ec933925309"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pp.png",
+ "bytes": 16266,
+ "sha256": "efbc1796cbeb321201318f8be76bf2f4bcb45491d99710180e1fc4566b271963"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n00.png",
+ "bytes": 13873,
+ "sha256": "aff1a64e71cdca04cbe72170feaa11e5284c57f2479703250981c7cd3510f1af"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0n.png",
+ "bytes": 16245,
+ "sha256": "1413954ca8edc01593679686edf534a55785207dd56ecbdbdd2a1f81bf1c6889"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0p.png",
+ "bytes": 16103,
+ "sha256": "aa614dd95fa6c33dc9650e91599b75a2ba7b634e884e43fc95f076dd198a850c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nn0.png",
+ "bytes": 17181,
+ "sha256": "8211b8045e0eddd5d15ba737cde7ccf4373cd19cd322b71c227e9db9ac29b85c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnn.png",
+ "bytes": 18333,
+ "sha256": "fd456e584c2d55ba70bb82c31ebdae54bd6050c52a1a5d77a1ae5a4e30be0d15"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnp.png",
+ "bytes": 18205,
+ "sha256": "4ac7ba2ab1c61280585304de0b1b343b2ce6d26177b2c830e4cca903d941dd21"
+ },
+ {
+ "filename": "eso0844a/impostors/view-np0.png",
+ "bytes": 16566,
+ "sha256": "f08f763fa3b91d78b5471c4c92f9c9eee005f730d0f135dd2ed3c946a3103072"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npn.png",
+ "bytes": 18179,
+ "sha256": "d4777b2ba664846faca602046e708e817d3e620617b3708029444f37520a979d"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npp.png",
+ "bytes": 17882,
+ "sha256": "1555e695cd4472ef5c8040e42f5af64b7adb30d5d48d9fd1640bd7611f490e0b"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p00.png",
+ "bytes": 13926,
+ "sha256": "4b3d0c279484f7fd46295037a60e41e081dd952ee23dfc4da54b2b3ea95c60c5"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0n.png",
+ "bytes": 16082,
+ "sha256": "4a8d2219e7753680c2a62ac94540737d38dace55b010179413c586a4d32a6dc9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0p.png",
+ "bytes": 16222,
+ "sha256": "42b8d608f6fce88844edebdc71820eb00a78bdb759eece0234be531ec242e189"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pn0.png",
+ "bytes": 16602,
+ "sha256": "05407bb1fe5bcafe2d1657f39c930055183bfd7aea3e7ec29cf1a4b6864f08e8"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnn.png",
+ "bytes": 17957,
+ "sha256": "5d3dff6ed46bdd9d02b524036cf15e54cb3235377e87f1092a9001425e5bac71"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnp.png",
+ "bytes": 18142,
+ "sha256": "04cc74f6a9e2120398b8c0d3804f175378e2dd50ca8e9e2c6c19f115a9bc81f4"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pp0.png",
+ "bytes": 17201,
+ "sha256": "e852ac472d7d640d6fbb134873e7cdd30d3ffbbc6a1560672c3c5d5ce9276fa9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppn.png",
+ "bytes": 18148,
+ "sha256": "394139a551defd9c14085f8e9177a1d106ec12de61724a855551462df3088259"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppp.png",
+ "bytes": 18310,
+ "sha256": "6e54542765e0daf109a7a93de5013efc0407eadd3c66139b7598598cff165349"
+ },
+ {
+ "filename": "eso1119b/atlases/x.webp",
+ "bytes": 717392,
+ "sha256": "7c140e5355dae4ee747b5033fbebbeb1e3065a60fbfab907c9a497c195c36ca8"
+ },
+ {
+ "filename": "eso1119b/atlases/y.webp",
+ "bytes": 665178,
+ "sha256": "b4303f0b696ebe2dcacc4d29cd16c076963e64ce9b83e6baaccc6d799c3d24d5"
+ },
+ {
+ "filename": "eso1119b/atlases/z.webp",
+ "bytes": 744376,
+ "sha256": "48cabfce2c49e49af9c1dcfc909279bda7c29e93e4c3dcee52bc4a1621281e62"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00n.png",
+ "bytes": 17819,
+ "sha256": "fc4b123e3af4cfd234ce5c9f0d0152f15332a11d856dc39b64d6b2ec99e33cff"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00p.png",
+ "bytes": 17808,
+ "sha256": "b3c41a3bc5e4b2e455a46a5f9b45a55f73818c9dd81492c0bf13f5c470a1c1cc"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0n0.png",
+ "bytes": 18194,
+ "sha256": "98795bd3eb6fff2b6725005a2d27ed77e956c314d0f5fbf8c49be78ffa42d1b6"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0nn.png",
+ "bytes": 20855,
+ "sha256": "3d3492f4abe86ed315bef89b4eed6ea12dde6e42a21127dde8dbd472afa4c09f"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0np.png",
+ "bytes": 21992,
+ "sha256": "d9becac304218245c0a81feb451e4938fa2738cee115795999110a1f3496608c"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0p0.png",
+ "bytes": 18135,
+ "sha256": "5c1c63b173cc7d66476ddb33bff80542a66b0ff5ae550aa54e1ce34c281fc2a3"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pn.png",
+ "bytes": 22003,
+ "sha256": "6b049f9dfddf5fcfa7b588776192479ff9e5f3dec6eba7e8b55729bcbcf11a77"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pp.png",
+ "bytes": 20827,
+ "sha256": "60e6c04a4bd0a75528ba788d6dfd0b765cf016d64eb317a30c277c329e0479fe"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n00.png",
+ "bytes": 18428,
+ "sha256": "bdbb5b335df7562f066a9b783f34af007123779136f787a6fe1e48d876df3f81"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0n.png",
+ "bytes": 21672,
+ "sha256": "b8374e6d60df50e5dfbd74b93e7afc720637052021a2c39498d7f679f0acc741"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0p.png",
+ "bytes": 21114,
+ "sha256": "17bafffbcf235dc9467ac0bc466d5190b43b376dda6a617fa83733d8ff52264d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nn0.png",
+ "bytes": 20813,
+ "sha256": "04574df58cc0371bcd9cbc504802abb32602736aca5ed78723c31e229c0fe3d2"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnn.png",
+ "bytes": 23385,
+ "sha256": "ba8db676f74fc73640634e80224852b89120b7458847d11e58ef6b4c6b58bb74"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnp.png",
+ "bytes": 23516,
+ "sha256": "59b45a749a76bc20b941251a563ab8f1ea41f6b25f6bb0d674308b09d597f729"
+ },
+ {
+ "filename": "eso1119b/impostors/view-np0.png",
+ "bytes": 20729,
+ "sha256": "644dc71d5c613fdbd1faf5ad662cc88572d8251308b5c62b0f9d8bc93c17d4fd"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npn.png",
+ "bytes": 24083,
+ "sha256": "2f256ea4a133442ce5dfdb564999c92e22825d3ecb4aa7964ebc120c4450c64c"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npp.png",
+ "bytes": 22900,
+ "sha256": "810e0f41aec1f714a15292a78560e1b8af8e21e4b6830b31ad5278d4092541a5"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p00.png",
+ "bytes": 18397,
+ "sha256": "eafbf2d83cf5d90187c429b8f2691aee84b8e8e1707f007398e6b38819982285"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0n.png",
+ "bytes": 21097,
+ "sha256": "50cef00922fc06c8b371031eb49d0b649b30973132272a2c1825cbe5a58536d8"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0p.png",
+ "bytes": 21643,
+ "sha256": "36898320964881289e223386a14a96f03c496db91828a226e2bee0f108c521a8"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pn0.png",
+ "bytes": 20712,
+ "sha256": "fc48f7402e52da24ceb72e20835c5f3721f2121d5cb346b1e9f57bd34258af7e"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnn.png",
+ "bytes": 22866,
+ "sha256": "b32298d848102bc899c2db9254ff43fb0d5716aaf074b7a3a003580ab6638610"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnp.png",
+ "bytes": 24053,
+ "sha256": "97fd71da3b4379b327435b54360bd950128998a28969d91576f19d64fb150b19"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pp0.png",
+ "bytes": 20832,
+ "sha256": "d155d4807b4efe66de898bc8863be49d9763c03629bbbf47a6b04c0fe82e29ed"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppn.png",
+ "bytes": 23507,
+ "sha256": "b16791ed70b6ca22bbd84ce8d9a295711c1b55cc8e1dd28dab6e968d535b9788"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppp.png",
+ "bytes": 23343,
+ "sha256": "cd8a0f7a94149a386a8b98ec8a47257539193ef9f612701309e32cc6def08212"
+ },
+ {
+ "filename": "lenses.json",
+ "bytes": 433153,
+ "sha256": "42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c"
+ },
+ {
+ "filename": "presentation.json",
+ "bytes": 4200,
+ "sha256": "69e809ee3e10109b51e9e3a2e8f159436a7038a43693e8820da396a90e0c53cc"
+ },
+ {
+ "filename": "provenance.json",
+ "bytes": 76924,
+ "sha256": "974bcfa338298a83ac0190a5668bf4e4d377bb5fbe90e93e04b27afaf256ffd9"
+ }
+ ],
+ "requests": [
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb/datasets/8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1/datasets/33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b62c0d4a8203fc424c1224ba7fb6a2c780f98abc25da2d3bd95e17d0145ec2ba/delivery.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7827406e40fef2a444bf1da7c92dbb73aea22132dc4da142cbddaa1c3038b954/eso0844a/impostors/view-00p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4156c57dd44f146b3de1b146f31da37f676ef5ca97c388fc03d3442064c3fcae/eso0844a/impostors/view-00n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f2cfe0f147d8dd07d66be1acb6442c9898e52a37426ed332818e704111ba2a6a/eso0844a/atlases/z.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2be14d24b7b8c9207982a44d0257d2d33e9e2f4c6f1a20e11e8e83a2d419ff3e/eso0844a/atlases/x.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e9d6d2605bd483e58d3ff86bb49f010a0dbb3e88bd19a7529f56623288d5bb90/eso0844a/impostors/view-0n0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f378d5167d5c82e2779c3507a67f5f3cc7d3cd67e8396a70a5e8d47d0bca3247/eso0844a/atlases/y.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/26161006830d714bee7e57dc84aa2bde9a22fb2d375d32188231d5b9fe6ca1d0/eso0844a/impostors/view-0nn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/aa614dd95fa6c33dc9650e91599b75a2ba7b634e884e43fc95f076dd198a850c/eso0844a/impostors/view-n0p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/089d623a473e4fa400428e882ed675afdfe55fa15d05c0dc05879dc5279f234c/eso0844a/impostors/view-0np.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3612a4bb86230a87d514ae5417aa57300768b1a10d61f482414dfae1d4806da9/eso0844a/impostors/view-0p0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/c8621057e346b5167a188ece82721e947bc5d06d358edc6883ab2ec933925309/eso0844a/impostors/view-0pn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/f08f763fa3b91d78b5471c4c92f9c9eee005f730d0f135dd2ed3c946a3103072/eso0844a/impostors/view-np0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d4777b2ba664846faca602046e708e817d3e620617b3708029444f37520a979d/eso0844a/impostors/view-npn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/efbc1796cbeb321201318f8be76bf2f4bcb45491d99710180e1fc4566b271963/eso0844a/impostors/view-0pp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4a8d2219e7753680c2a62ac94540737d38dace55b010179413c586a4d32a6dc9/eso0844a/impostors/view-p0n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/aff1a64e71cdca04cbe72170feaa11e5284c57f2479703250981c7cd3510f1af/eso0844a/impostors/view-n00.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1413954ca8edc01593679686edf534a55785207dd56ecbdbdd2a1f81bf1c6889/eso0844a/impostors/view-n0n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4ac7ba2ab1c61280585304de0b1b343b2ce6d26177b2c830e4cca903d941dd21/eso0844a/impostors/view-nnp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5d3dff6ed46bdd9d02b524036cf15e54cb3235377e87f1092a9001425e5bac71/eso0844a/impostors/view-pnn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/8211b8045e0eddd5d15ba737cde7ccf4373cd19cd322b71c227e9db9ac29b85c/eso0844a/impostors/view-nn0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/fd456e584c2d55ba70bb82c31ebdae54bd6050c52a1a5d77a1ae5a4e30be0d15/eso0844a/impostors/view-nnn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/1555e695cd4472ef5c8040e42f5af64b7adb30d5d48d9fd1640bd7611f490e0b/eso0844a/impostors/view-npp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/7c140e5355dae4ee747b5033fbebbeb1e3065a60fbfab907c9a497c195c36ca8/eso1119b/atlases/x.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/4b3d0c279484f7fd46295037a60e41e081dd952ee23dfc4da54b2b3ea95c60c5/eso0844a/impostors/view-p00.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/05407bb1fe5bcafe2d1657f39c930055183bfd7aea3e7ec29cf1a4b6864f08e8/eso0844a/impostors/view-pn0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/42b8d608f6fce88844edebdc71820eb00a78bdb759eece0234be531ec242e189/eso0844a/impostors/view-p0p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/e852ac472d7d640d6fbb134873e7cdd30d3ffbbc6a1560672c3c5d5ce9276fa9/eso0844a/impostors/view-pp0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/04cc74f6a9e2120398b8c0d3804f175378e2dd50ca8e9e2c6c19f115a9bc81f4/eso0844a/impostors/view-pnp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/394139a551defd9c14085f8e9177a1d106ec12de61724a855551462df3088259/eso0844a/impostors/view-ppn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b4303f0b696ebe2dcacc4d29cd16c076963e64ce9b83e6baaccc6d799c3d24d5/eso1119b/atlases/y.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6e54542765e0daf109a7a93de5013efc0407eadd3c66139b7598598cff165349/eso0844a/impostors/view-ppp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/fc4b123e3af4cfd234ce5c9f0d0152f15332a11d856dc39b64d6b2ec99e33cff/eso1119b/impostors/view-00n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/48cabfce2c49e49af9c1dcfc909279bda7c29e93e4c3dcee52bc4a1621281e62/eso1119b/atlases/z.webp",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/98795bd3eb6fff2b6725005a2d27ed77e956c314d0f5fbf8c49be78ffa42d1b6/eso1119b/impostors/view-0n0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/3d3492f4abe86ed315bef89b4eed6ea12dde6e42a21127dde8dbd472afa4c09f/eso1119b/impostors/view-0nn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b8374e6d60df50e5dfbd74b93e7afc720637052021a2c39498d7f679f0acc741/eso1119b/impostors/view-n0n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b3c41a3bc5e4b2e455a46a5f9b45a55f73818c9dd81492c0bf13f5c470a1c1cc/eso1119b/impostors/view-00p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d9becac304218245c0a81feb451e4938fa2738cee115795999110a1f3496608c/eso1119b/impostors/view-0np.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/17bafffbcf235dc9467ac0bc466d5190b43b376dda6a617fa83733d8ff52264d/eso1119b/impostors/view-n0p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/04574df58cc0371bcd9cbc504802abb32602736aca5ed78723c31e229c0fe3d2/eso1119b/impostors/view-nn0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/5c1c63b173cc7d66476ddb33bff80542a66b0ff5ae550aa54e1ce34c281fc2a3/eso1119b/impostors/view-0p0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/bdbb5b335df7562f066a9b783f34af007123779136f787a6fe1e48d876df3f81/eso1119b/impostors/view-n00.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/60e6c04a4bd0a75528ba788d6dfd0b765cf016d64eb317a30c277c329e0479fe/eso1119b/impostors/view-0pp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/6b049f9dfddf5fcfa7b588776192479ff9e5f3dec6eba7e8b55729bcbcf11a77/eso1119b/impostors/view-0pn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/36898320964881289e223386a14a96f03c496db91828a226e2bee0f108c521a8/eso1119b/impostors/view-p0p.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/59b45a749a76bc20b941251a563ab8f1ea41f6b25f6bb0d674308b09d597f729/eso1119b/impostors/view-nnp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/ba8db676f74fc73640634e80224852b89120b7458847d11e58ef6b4c6b58bb74/eso1119b/impostors/view-nnn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/2f256ea4a133442ce5dfdb564999c92e22825d3ecb4aa7964ebc120c4450c64c/eso1119b/impostors/view-npn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/644dc71d5c613fdbd1faf5ad662cc88572d8251308b5c62b0f9d8bc93c17d4fd/eso1119b/impostors/view-np0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/810e0f41aec1f714a15292a78560e1b8af8e21e4b6830b31ad5278d4092541a5/eso1119b/impostors/view-npp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/50cef00922fc06c8b371031eb49d0b649b30973132272a2c1825cbe5a58536d8/eso1119b/impostors/view-p0n.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/fc48f7402e52da24ceb72e20835c5f3721f2121d5cb346b1e9f57bd34258af7e/eso1119b/impostors/view-pn0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b32298d848102bc899c2db9254ff43fb0d5716aaf074b7a3a003580ab6638610/eso1119b/impostors/view-pnn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/b16791ed70b6ca22bbd84ce8d9a295711c1b55cc8e1dd28dab6e968d535b9788/eso1119b/impostors/view-ppn.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/eafbf2d83cf5d90187c429b8f2691aee84b8e8e1707f007398e6b38819982285/eso1119b/impostors/view-p00.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/d155d4807b4efe66de898bc8863be49d9763c03629bbbf47a6b04c0fe82e29ed/eso1119b/impostors/view-pp0.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/97fd71da3b4379b327435b54360bd950128998a28969d91576f19d64fb150b19/eso1119b/impostors/view-pnp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c/lenses.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/cd8a0f7a94149a386a8b98ec8a47257539193ef9f612701309e32cc6def08212/eso1119b/impostors/view-ppp.png",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/69e809ee3e10109b51e9e3a2e8f159436a7038a43693e8820da396a90e0c53cc/presentation.json",
+ "status": 200
+ },
+ {
+ "url": "https://earth-assets.lowpoly.cc/runtime-assets/974bcfa338298a83ac0190a5668bf4e4d377bb5fbe90e93e04b27afaf256ffd9/provenance.json",
+ "status": 200
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/renderer-mutation.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/renderer-mutation.json
new file mode 100644
index 0000000000..210cb44bdc
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/renderer-mutation.json
@@ -0,0 +1,14 @@
+{
+ "mutation": "add one retained DOM leaf per slab in isolated renderer copy",
+ "rendererTestsPassed": 14,
+ "baselinePassed": 2,
+ "mutationExit": 1,
+ "failedBudgetCases": 2,
+ "actualAssertions": [
+ "expected604leaves to be453",
+ "expected600leaves to be450"
+ ],
+ "sharedRendererUnchanged": true,
+ "rendererSourceSha256": "0612a2aa83bb116a133c9c0178e685f741d038fb8e6f6e599470c9ad7f70fd48",
+ "testSha256": "1890c38e736381678d411c69a111bc628d6f06c008cd72786e1cca656543e759"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/renderer-wrapper-mutation.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/renderer-wrapper-mutation.json
new file mode 100644
index 0000000000..9be912e54e
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/renderer-wrapper-mutation.json
@@ -0,0 +1,7 @@
+{
+ "mutation": "add one wrapper to app bank root in isolated renderer copy",
+ "exitCode": 1,
+ "failedBudgetCases": 2,
+ "observed": "expected500 to be499 in both cases",
+ "conclusion": "The exact inclusive overhead assertion does lock current overhead, including the one-element reserved margin. No producer/test source fix needed."
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/final-source-checkpoint.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/final-source-checkpoint.json
new file mode 100644
index 0000000000..d1e1f18224
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/final-source-checkpoint.json
@@ -0,0 +1,30 @@
+{
+ "src/renderers/css/volume/prepared-volume-lenses.test.ts": {
+ "sha256": "1890c38e736381678d411c69a111bc628d6f06c008cd72786e1cca656543e759",
+ "bytes": 27841
+ },
+ "tools/nebula/application/element-budget.ts": {
+ "sha256": "9d0678bee9988b51b2afbefe874ce92feabecc369a63403a58f03946af592a28",
+ "bytes": 3096
+ },
+ "tools/nebula/application/objects.ts": {
+ "sha256": "3af766116ebc0436162b13db781b851b69b4060f470ff79e36a1cd3912bb04d6",
+ "bytes": 23232
+ },
+ "tools/nebula/application/element-budget.cases.ts": {
+ "sha256": "435af09c9f369ccbc579006fb20fd0ef0bc18f6a5a466729175977e830ab223c",
+ "bytes": 6506
+ },
+ "tools/nebula/application/objects.cases.ts": {
+ "sha256": "80ee489ed7724993d14aafa766e77b8fa3723af22cea00626011ee45f51e4fdb",
+ "bytes": 11547
+ },
+ "tools/nebula/application/element-budget.test.ts": {
+ "sha256": "500c59b9e5ce79dd9912a21be84c400f501c9222ad54fa3296bb6634bb43256b",
+ "bytes": 1717
+ },
+ "tools/nebula/application/objects.test.ts": {
+ "sha256": "cfe387a0db16c48988f29a95ad32914638cc667fb03a02b227386aba52cc63c5",
+ "bytes": 1696
+ }
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-authoring-replay.md b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-authoring-replay.md
new file mode 100644
index 0000000000..22cb36bf74
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-authoring-replay.md
@@ -0,0 +1,25 @@
+**Verdict: pass for this packet.** No P0, P1, or borderline-P2 blockers in authoring admission or compact replay transport. The 500-element number is a retained-DOM certificate, not visual acceptance.
+
+New authoring fails closed without a host profile, reserves stars before the planner, and will not treat explicit old slice counts as a quota hole. Historical receipts without a budget keep their bytes and do not mint a certificate. Host CSS profile is injected at Lab/application adapters; core bake stays generic.
+
+### What holds
+
+- Default planning uses `reservedStars` then `maximumRenderSlabs` before `optimizeVolumeLayers` (`bake.ts` 168–170, 102–112). Tests: 445 stars die before sampling; 3 stars → `maximumLayers` 150.
+- Explicit new sampling without a receipt still runs `createRenderElementBudget` (`bake.ts` 99–100). 50+50+52 → 503, rejected in core and in the real CSS `bakeCompiler` fixture.
+- Historical path: no saved `renderBudget` → clone only, optional over-budget message, no new receipt (`bake.ts` 91–97). M42 test keeps exact JSON absence. Compact compiler/sampled set `historicalReplay: sampling.renderBudget === undefined`.
+- A receipt present in historical mode still validates (`compiler-sampling.test.ts` 62–64).
+- Sampled unions pass `reservedStars: original.stars.length` so component bakes cannot under-count the final star bank (`sampled.ts` 225–226). `readRenderElementBudget` allows `actualStars <= reserved`.
+- Profile owner is CSS (`compiler-render-budget.ts`, `css-volume.ts` re-export, Lab bake, compact adapters, `nebulaBakeBackend`). Core only consumes `backend.renderBudget`.
+
+### Ordinary P2 (not blocking)
+
+- Leftover `Math.min(500, maximumRenderSlabs)` is a layer cap on top of the element budget, not a bypass.
+- Compact Lab adapters import the CSS profile file directly instead of the `css-volume` re-export; still not a core/bake renderer import.
+
+### Uncertainty (parent-owned)
+
+- This packet does not prove Omega’s mounted object root is ≤500. Profile is one topology / ≤26 impostors / no occulting front root; it does not certify an arbitrary app bank. Renderer 14/14 and extra-leaf mutation red are independent of this transport.
+
+Required verification remains: parent mount count on the Omega object root; do not treat a budget receipt as scientific fit.
+
+=== REPORT COMPLETE ===
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-ci-transport.md b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-ci-transport.md
new file mode 100644
index 0000000000..572f02e1d8
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-ci-transport.md
@@ -0,0 +1,13 @@
+**P1 — child success is asserted against TAP summary lines, but the child is spawned with Node’s default reporter.**
+
+`element-budget.test.ts` 23–29 and `objects.test.ts` 23–29 run `spawnSync(process.execPath, ['--test', outfile], …)` with no `--test-reporter` (and no TAP-forcing env). Node 22’s default is `spec`. Piped stdout is not a TTY; spec still prints `ℹ tests N` / `ℹ pass N` / `ℹ fail 0`, not `# tests N`.
+
+The wrappers then `assert.match(output, /# tests 9\b/)` (and `6`) plus `# pass` / `# fail 0`. A fully passing child therefore fails the wrapper; a failing child is still caught by `result.status === 0`, but the required positive counts never lock.
+
+That is not a launch-only wrapper in the success path: it can go red without a case failure, and it never proves 9/6 under the reporter CI actually gets.
+
+**Fix at this layer:** pass `'--test-reporter', 'tap'` (or `'--test-reporter=tap'`) on the child argv so the existing `# tests` / `# pass` / `# fail` assertions match what the child prints. Do not rely on inherited env.
+
+No P0. No other borderline P2 on execute/assert/failure-surfacing: `NODE_TEST_CONTEXT` is stripped so this is a real nested runner; esbuild throw, spawn `error`, non-zero status, and wrong counts all fail the parent test; cases are the bundled entry, not a stub.
+
+=== REPORT COMPLETE ===
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-delivery-guard.md b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-delivery-guard.md
new file mode 100644
index 0000000000..9912ff862d
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-delivery-guard.md
@@ -0,0 +1,20 @@
+**Admit-only-complete-new-compiler-banks after full assembly: yes.** No P0/P1/borderline-P2 on this installer boundary.
+
+The guard runs on the assembled bank (`objects.ts` after impostors, atlases, field-star replacement, `validatePreparedVolumeLenses`), throws before `rename` into `prepared/`, and only then writes `renderElements`. Historical sampling without `renderBudget` returns `undefined` and does not stamp a quota.
+
+### Cost / admission (evidence)
+
+- Inclusive total is `17 + (impostors object ? 3 : 0) + viewCount + 3×actualLeaves + 1×stars`, then `> 500` rejects (`element-budget.ts` 9, 21–37). Zero impostors is `17` overhead, not `46` (unit test 50–52: `470`).
+- Saved receipt is checked with **planned** XYZ (`readRenderElementBudget(..., plannedSlabs)`), actual leaves may be lower; actual > planned rejects. Stars may be ≤ reservation; extra stars fail reservation before the cap.
+- Occulting roots, second topology, and `> 26` views are rejected. Compiler-only `compilerSampling`; other methods never set it.
+- Real installer test: extra field star fails `/star reservation/`, existing `object.json` / `prepared/lenses.json` unchanged; reserved final count installs atlas + 26 views and records `{ totalElements: 56 }`.
+
+### In-scope P2 (not blocking)
+
+- Planner `reservedElements: 47` is not the installer’s inclusive formula (`46` with 26 views, `17` with none). Admission still uses the actual formula and the 500 cap; this is a documented one-element planning spare, not a wrong accept/reject of the tested profile.
+
+No concrete wrong acceptance of an over-cap new compiler bank, no wrong rejection of the tested zero-impostor/pruned/single-topology profile, no missing fail-before-replace on this path.
+
+**Verdict:** accept.
+
+=== REPORT COMPLETE ===
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-renderer-accounting.md b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-renderer-accounting.md
new file mode 100644
index 0000000000..c664ac0e7c
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/grok-renderer-accounting.md
@@ -0,0 +1,76 @@
+**Verdict: not admitted for Omega.** This round certifies a synthetic one-topology CSS bank at **499** nodes with a **1-element reserve**, not the live object and not compiler authoring.
+
+The 500 cap is a DOM quota, not visual acceptance. Renderer physics is unchanged (receipt). Historical replay is not given a certificate in these files.
+
+---
+
+### What the numbers actually are
+
+Profile `css-volume-single-topology@1`: `maximumElements=500`, `elementsPerSlab=3`, `elementsPerStar=1`, `reservedElements=47` (`compiler-render-budget.ts` 5–8).
+
+Count: `47 + stars + 3×slabs` (`render-element-budget.ts` 29–32, 42–46). Stars are subtracted **before** slabs (`maximumRenderSlabs` 36–40).
+
+Measured mount (FakeElement, object root only):
+
+| slabs | stars | copies | overhead | total |
+|---|---|---|---|---|
+| 151 | 0 | 453 | 46 | **499** |
+| 150 | 3 | 450 | 46 | **499** |
+
+That matches the test (`prepared-volume-lenses.test.ts` 106–113). Predicted total is **500**; mount is **499**. Shell/`before` are outside `descendants(root)`.
+
+26 impostors, XYZ×3 copies, hidden nodes, empty star wrapper are in that 499 (108–110, 127). `volumeResidentDomNodes` matches (`prepared-volume-lenses.ts` 202–208).
+
+No growth on that payload: same node identities across `selectLens` / `publish` / star toggle, `volumeResidentTopologyCount === '1'` (115–125). All three lenses share topology in `budgetPayload` (87–97), so `ensureFamily` does not allocate a second cloud (210–213, 293–304).
+
+Lab uses `mountPreparedCssVolume` only (`volume-renderer.ts` 38–39): no LOD, no 26 impostors, no lens bank. Inclusive cost is **smaller** than 46, so the **same** slab/star counts still sit under 500. A looser lab-only slab count is not shown.
+
+---
+
+### P0
+
+None in this packet for the **declared** profile (one topology, ≤26 impostors, no front root).
+
+---
+
+### P1
+
+1. **Authoring is not in the change.** Requirement: default compiler reserves **actual stars**, then slabs. Only the contract + a hand-built test payload call `createRenderElementBudget` (`prepared-volume-lenses.test.ts` 105, 112). Production bake/viewer (`compiler-viewer.ts` 26–50, 78–96) does not. Quota cannot stop Omega from shipping a fatter bank yet.
+
+2. **499 is not Omega admission.** Packet already assigns that to a real browser count. FakeElement + `querySelectorAll = []` never exercises adoption, extra anonymous nodes, or a second object.
+
+3. **This profile does not cover the product if Omega is not that topology.** A second topology is still retained hidden at 52 nodes (`prepared-volume-lenses.test.ts` 198–215; `ensureFamily` 210–225). Occulting `frontRoot` is extra (`prepared-volume-lenses.ts` 175–182, 208) and **uncertified**. Treating 499 as “Omega stays ≤500” would be false if either path is live.
+
+---
+
+### Borderline P2
+
+**Overhead slack is real, not measured-tight.** Reserved 47 vs inclusive 46, assert `length === 499` and `<= 500`. One extra wrapper still fits 151/150 slabs. Mutation only failed **per-slab copies** (`604` vs `453`, `600` vs `450`) — not a +1 wrapper. Intentional reserve, but it is not a lock on overhead.
+
+---
+
+### Ordinary P2 (non-blocking)
+
+- Multi-topology test documents growth; it does not fail a 500 cap.
+- `readRenderElementBudget` can reserve more stars than mounted (`render-element-budget.ts` 48–51); honest for union banks, unused by these tests.
+- Lab has no profile of its own; it only “fits” because it is cheaper.
+
+---
+
+### Uncertainty
+
+- Live Omega topology count, occulting front host, and whether historical bakes get a `cssearth-render-element-budget@1` field.
+- Whether round 2 wires `maximumRenderSlabs` on the real compiler. Until then, replay of old bytes is honest **only if** nothing writes a new budget onto them (not shown).
+
+---
+
+### Required verification (admission, not this unit file)
+
+1. Real Omega: inclusive **element** count of that object’s root+descendants (copies, stars, wrappers, hidden, impostors; no shell). Must be ≤500 after camera, lens/material, and star visibility changes, with **no node growth**.
+2. Default compiler: `starCount` reserved, **then** `slabCount ≤ maximumRenderSlabs`; reject `< 3` complete XYZ.
+3. Historical immutable bytes: load without minting this profile/budget.
+4. Lab mount of the same bake: inclusive count ≤500 (expected easier).
+
+Mutation already shows extra per-slab DOM fails copies; keep a check that **total** `descendants` exceeds 500 if overhead or copies grow.
+
+=== REPORT COMPLETE ===
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/receipt.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/receipt.json
new file mode 100644
index 0000000000..49b3e878e4
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/receipt.json
@@ -0,0 +1,42 @@
+[
+ {
+ "packet": "renderer-accounting",
+ "durationMs": 83452,
+ "turns": 1,
+ "successful": true,
+ "completeSentinel": true,
+ "emptyTools": true,
+ "emptyMcp": true,
+ "sourceDrift": []
+ },
+ {
+ "packet": "authoring-replay",
+ "durationMs": 57587,
+ "turns": 1,
+ "successful": true,
+ "completeSentinel": true,
+ "emptyTools": true,
+ "emptyMcp": true,
+ "sourceDrift": []
+ },
+ {
+ "packet": "delivery-guard",
+ "durationMs": 63241,
+ "turns": 1,
+ "successful": true,
+ "completeSentinel": true,
+ "emptyTools": true,
+ "emptyMcp": true,
+ "sourceDrift": []
+ },
+ {
+ "packet": "ci-transport",
+ "durationMs": 63657,
+ "turns": 1,
+ "successful": true,
+ "completeSentinel": true,
+ "emptyTools": true,
+ "emptyMcp": true,
+ "sourceDrift": []
+ }
+]
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/verified.md b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/verified.md
new file mode 100644
index 0000000000..cea72dc9f4
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/review/verified.md
@@ -0,0 +1,44 @@
+# Retained DOM quota review, implementation round 1/3
+
+The authored quota and renderer conformance changes have no verified introduced P0/P1/borderline P2 blocker. Live Omega admission remains parent-owned and pending its newly baked artifact and actual Lab/app counts; these unit tests and receipts do not constitute scientific visual acceptance.
+
+All four bounded Grok packets completed successfully, with nonempty terminal reports and completion sentinels, empty tools/MCP, and source hashes unchanged. Raw findings are preserved without rewriting them. Scope covers the pure profile/receipt contract, star reservation before default planning, explicit-plan admission, immutable compact replay, real Lab/app backend injection, and retained renderer accounting tests. It excludes source fitting and scientific handoff.
+
+## Verified findings disposition
+
+1. Renderer packet P1 “authoring is not in the change”: this is an omitted-packet scope inference, not a source defect. The second packet contains actual bake.ts admission: actual/reserved stars at168–170, profile-required default path and maximumRenderSlabs passed into optimization at102–112. That review reports no authoring/replay blockers. The actual CSS adapter negative rejects152 slabs before sampling.
+2. Renderer packet P1 “499 is not Omega admission”: correct acceptance boundary, already assigned to the parent. Our test calls the real bank mount against a DOM stand-in; it does not claim live browser adoption or candidate acceptance. Parent must count the actual object inclusively through lenses/cameras and confirm one topology,26 impostors and no front root. The Lab mount must also remain<=500.
+3. Renderer packet P1 multi-topology/front-root concern: profile explicitly excludes those layouts. Existing topology test pins29→52 retained nodes, including the hidden family. The generic bank remains unchanged; the selected candidate's actual browser gate must establish this profile's applicability.
+4. Renderer packet borderline P2 “overhead is not locked”: disproved by source and a second isolated mutation. The test asserts exact499 inclusive nodes and exact46 overhead. Adding one wrapper makes both cases fail `expected500 to be499`; it does not silently consume the safety margin. Existing extra-leaf mutation also fails both cases604vs453 and600vs450. No producer/test change was needed to close this reviewer claim.
+5. Ordinary observations are non-actionable in this scope: Math.min(500, remainingSlabs) retains the existing planner's maximum layer bound and cannot bypass the stricter element budget; direct profile imports remain in renderer-owned host adapters; reserved stars can exceed temporary material-bank stars because final component unions restore them. The Lab has fewer wrappers than the stricter delivery profile. No additional architecture is required.
+
+## Evidence
+
+- Renderer mounting14/14 pass; two near-limit variants each499 inclusive, with hidden copies/impostors/stars counted and stable same-topology nodes across lenses, camera axes/oblique/opposite/far-near and star toggles.
+- Isolated extra-leaf and extra-wrapper mutations each cause two explicit numeric assertion failures. Shared renderer physics source was untouched.
+- Compiler/core/dependent31/31 pass; optimizer-limit mutation fails two actual default paths with623/626 reserved elements rejected by the final receipt guard.
+- Core+bake+Lab types and package-boundary sentinel pass. Root test graph1758 checks renderer tests; it excludes Lab package test roots. A separate narrow strict config checks the actual affected core, bake and Lab backend test roots; all three pass with no diagnostics (renderer-budget-round1-nebula-test-types.log).
+- Existing historical M42 sampling retains exact serialized absence of layer/budget fields while reporting its over-budget cost. Full old scientific replay is not claimed here.
+- CI adds the two genuine new test selectors without weakening any check. Skill/maintained method prose says retained scene elements and preserves scientific gates; no generated scene or source changes were made by this reviewer.
+
+## Final completed-bank installation guard review
+
+Grok delivery-guard report completed with empty tools/MCP and the required terminal sentinel. It found no P0/P1/borderline P2 and accepted the scoped boundary. Its ordinary P2 observation merely restates the documented one-element planning reserve; actual admission correctly records46 overhead with26 impostors and17 without. No producer change follows that non-actionable observation.
+
+The guard rejects occulting roots, multiple retained topologies, >26 impostors, per-axis growth beyond the saved plan, mismatched host profile/counters, and final stars beyond the reservation. It runs after real field-star replacement, impostor creation and atlas packing, before replacement of the current package. It reports actual pruned leaves and preserves historical absence without stamping a new certificate.
+
+Guard9 + installer6 tests pass; deleting the real installer admission call makes the field-star case fail with `Missing expected rejection`. The earlier zero-impostor actual-count issue was corrected in round2 to17 plus optional3 LOD elements, with the470 fixture. Strict selected-source/test types pass.
+
+The native GitHub direct-Node entry path uncovered a separate test-loader mismatch. The implementation owner moved the typed assertions to case files and added direct-Node esbuild runners using the existing preparation configuration. The local execution of the exact maintained CI command now passes 22/22 (`output/pr390-audit/element-budget/exact-ci-tests-final.log`), including both runners confirming all 9 + 6 inner cases. The reviewed assertions are preserved; the only case-body edit fixes an outdated discovery comment.
+
+## Actual candidate numerical replay evidence
+
+The independently supervised ordinary cold replay completed in232.932 seconds with code0, positive verified status and FS/network guard active. Its exact saved151-slab plan retains149 nontransparent XYZ leaves. Current final installer receipt records0 stars,149 slabs,26 impostors and493 inclusive elements. Raw447 PNG replay pins and59 final lens/texture files match the completed candidate; source inputs are unchanged. The separate actual Lab browser receipt passes across lenses/orbit/zoom/refresh with462 inclusive elements and no errors/blocked requests. These numerical receipts are not a scientific visual certificate, and native application browser acceptance remains the parent's separate gate.
+
+## Final CI transport disposition
+
+The final two runner files received a separate bounded Grok review with empty tools/MCP and a complete terminal sentinel. Its only claimed P1 was an incorrect assertion that Node 22 defaults to a spec reporter even with piped stdout. The actual maintained workflow pins Node 22, and the exact local CI command under Node 22.23.2 emits TAP and passes 22/22 (`exact-ci-tests-final.log`; the earlier log intentionally retains the pre-refresh metadata failure). Its two real child processes emit the required `# tests 9` / `# pass 9` and `# tests 6` / `# pass 6` summaries. This directly disproves the claimed failure; no speculative reporter change is required.
+
+The wrappers remove inherited NODE_TEST_CONTEXT, compile the actual strict typed case files, execute the real Node test child with a timeout, and reject compilation errors, spawn errors, nonzero exit, or missing/incorrect positive counters. They do not replace or skip installer assertions. Production sources remained unchanged during this transport correction. The renderer conformance fixture remains at SHA2561890c38e736381678d411c69a111bc628d6f06c008cd72786e1cca656543e759.
+
+No verified P0, P1, borderline P2 or actionable in-scope P2 remains in this bounded quota code review. Parent-owned final site/browser/R2/publication checks remain distinct, and scientific visual handoff remains unaccepted.
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/validation.json b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/validation.json
new file mode 100644
index 0000000000..af3f031049
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/validation.json
@@ -0,0 +1,1319 @@
+{
+ "schema": "cssearth-omega-element-budget-evidence@1",
+ "recordedAt": "2026-09-20T23:05:13.168Z",
+ "baseRevision": "0e22ec624e42986944cd914c7ddb88c84e3ef958",
+ "testedState": "Base revision plus sourcePins; tests and captures precede the commit that retains this report. Original field fit is reused; new bank qualification is recorded separately.",
+ "sourcePins": [
+ {
+ "path": ".github/workflows/universe.yml",
+ "bytes": 36948,
+ "sha256": "80927298ee9103f682640c41e6dab4052a839f43a6516f2293fdf8c577ecf5f1"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/delivery/compact-compiler.ts",
+ "bytes": 1362,
+ "sha256": "ceae17acf80fe487e1a5e5923d38a5671e5409ae986a5b80b2319aa62a002067"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/delivery/compact-sampled.ts",
+ "bytes": 7348,
+ "sha256": "efa3fa10cac440fe2a4dd8a38283315a7fe58410e498c2a98c22f659385c56ee"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/preparation/css-volume.ts",
+ "bytes": 407,
+ "sha256": "ff2e403521e695afd4b1ced22b177e888c8220024e30c4a8ff3117fe2c4a3616"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/bake.test.ts",
+ "bytes": 6532,
+ "sha256": "ce026d64be3724ec2af7c3e18d0385084aa84d425f3da9f677e71ab26c5b652c"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/bake.ts",
+ "bytes": 824,
+ "sha256": "1b7a185027c89f8f3bd529e6c791eec551b047881ed2995d468b313b6323da12"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compact-inputs/compiler.ts",
+ "bytes": 8788,
+ "sha256": "2b788e4cb4f2b7b85ac6e9134457f540db4940d7f929e06beef82546a4c8be20"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compact-inputs/sampled.ts",
+ "bytes": 12102,
+ "sha256": "c5c733bcc6225cef08279c6a876bd8391181951b598069a2834ca6b88cefc745"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/bake.ts",
+ "bytes": 21310,
+ "sha256": "004bbf282306ea47aa37abed065f5118663d4de18783c51b5ecaa6c3aea24579"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/compiler-sampling.test.ts",
+ "bytes": 7824,
+ "sha256": "9c93e885ec7096aa60dedd3444af2e97887811b50fa85b9fb06bcbd26e521c21"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/package.json",
+ "bytes": 4100,
+ "sha256": "767f04fabfceab60608e9ee5fedf637392ee026b3ec5066ff9e59dbdb01a35af"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts",
+ "bytes": 12864,
+ "sha256": "f85da67f5a3ea68a529ae0b449c9a83e991a9b37a926ec54604991a71cd1a4b8"
+ },
+ {
+ "path": "src/objects/omega-centauri/object.json",
+ "bytes": 1116,
+ "sha256": "e709e55c5ec0ad4f4846303cffec0d6c34c235554a69e5a8481535e9465e4255"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared-assets.json",
+ "bytes": 10592,
+ "sha256": "ac43536da6c0b558e221253a673b14c30dcecd61d5a504c38936ef307351a448"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs.json.gz",
+ "bytes": 2229420,
+ "sha256": "c9f2535b13f4b4745179ec53b63240df1207ef7f38a4ea34dff76cba50a74d68"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/delivery.json",
+ "bytes": 2428,
+ "sha256": "d1e34564e98eae6ebe9d8d59b8cfd4ebe19c20e0c14f8279c72444f96faf36a5"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/manifest.json",
+ "bytes": 11285,
+ "sha256": "4f77c2b73967a4ef7f164842b0b46ea5156ab690851a3bcd7047b5f3f49bdacd"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/presentation.json",
+ "bytes": 6277,
+ "sha256": "3096d25d13618cb24f18e731c8d8980ed424b9acb13792ff1439d090ccea85f8"
+ },
+ {
+ "path": "src/renderers/css/volume/prepared-volume-lenses.test.ts",
+ "bytes": 27841,
+ "sha256": "1890c38e736381678d411c69a111bc628d6f06c008cd72786e1cca656543e759"
+ },
+ {
+ "path": "tests/objects/browser/omega-centauri/native-focus-browser.mts",
+ "bytes": 10829,
+ "sha256": "97554907fc6fb9519ba76eda26527a09187bdf8d7c0c37157aab3fe79710c009"
+ },
+ {
+ "path": "tools/nebula/application/backend.ts",
+ "bytes": 974,
+ "sha256": "b70550d2f3db3d5a41125425cbf0556f50468c68fd765cc7b06f2dcb5e925d4f"
+ },
+ {
+ "path": "tools/nebula/application/objects.test.ts",
+ "bytes": 1696,
+ "sha256": "cfe387a0db16c48988f29a95ad32914638cc667fb03a02b227386aba52cc63c5"
+ },
+ {
+ "path": "tools/nebula/application/objects.ts",
+ "bytes": 23232,
+ "sha256": "3af766116ebc0436162b13db781b851b69b4060f470ff79e36a1cd3912bb04d6"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/render-element-budget.test.ts",
+ "bytes": 2042,
+ "sha256": "e4cade4f12c2f7e43c310be74c9b95a42f83d773ab46a7df2440d4eb8040282d"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/render-element-budget.ts",
+ "bytes": 4298,
+ "sha256": "ebde15faebea147d751ae75cfe62be549e1b81392a0982915dea8efc982cdc58"
+ },
+ {
+ "path": "src/renderers/css/volume/compiler-render-budget.ts",
+ "bytes": 595,
+ "sha256": "68acdb48f890c5624a819a5a215d242284c8e9603d61aa8a5ca75306015c7b63"
+ },
+ {
+ "path": "tools/nebula/application/element-budget.cases.ts",
+ "bytes": 6506,
+ "sha256": "435af09c9f369ccbc579006fb20fd0ef0bc18f6a5a466729175977e830ab223c"
+ },
+ {
+ "path": "tools/nebula/application/element-budget.test.ts",
+ "bytes": 1717,
+ "sha256": "500c59b9e5ce79dd9912a21be84c400f501c9222ad54fa3296bb6634bb43256b"
+ },
+ {
+ "path": "tools/nebula/application/element-budget.ts",
+ "bytes": 3096,
+ "sha256": "9d0678bee9988b51b2afbefe874ce92feabecc369a63403a58f03946af592a28"
+ },
+ {
+ "path": "tools/nebula/application/objects.cases.ts",
+ "bytes": 11547,
+ "sha256": "80ee489ed7724993d14aafa766e77b8fa3723af22cea00626011ee45f51e4fdb"
+ }
+ ],
+ "runtimePins": [
+ {
+ "path": "src/renderers/css/volume/prepared-volume-runtime.ts",
+ "bytes": 15387,
+ "sha256": "0612a2aa83bb116a133c9c0178e685f741d038fb8e6f6e599470c9ad7f70fd48"
+ },
+ {
+ "path": "src/renderers/css/volume/prepared-volume-lod.ts",
+ "bytes": 9536,
+ "sha256": "92ffeb184347248935e31602ad7cab49122767aa45837a041ca009d79285ce39"
+ },
+ {
+ "path": "src/renderers/css/volume/prepared-volume-lenses.ts",
+ "bytes": 21480,
+ "sha256": "1d20679ab004583ee1107ec07a09a62e5f7f5bed5438ba8062fe42c1783b09fa"
+ },
+ {
+ "path": "src/renderers/css/styles/volume.css",
+ "bytes": 1047,
+ "sha256": "cb09de8ee75df62aeb3775d6d435a955586d04d34ce19e009c2a43c4758d58cd"
+ },
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "bytes": 13264,
+ "sha256": "67286a0589f42d990d467d5c1e199661bc8b98b7102d152c527f34fc095e4a36"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.ts",
+ "bytes": 873,
+ "sha256": "12a727775a8277fede7cd743642b960a30c10a7334f2e47186d97b6e8d5413f4"
+ }
+ ],
+ "bake": {
+ "id": "59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e",
+ "elapsedSeconds": 287.174715917,
+ "derivation": {
+ "previousCompactSha256": "869e9ab24b644c4ea7d04cf2d4e2c719445c205b879eaebf13a420c7d7d75eac",
+ "originalResult": {
+ "path": ".local/nebula-lab/compiler/5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9/result.json",
+ "sha256": "0839b8149a37b7ca2b4d54434fd5881a8e2e27eb0da451e5d028e3a0637b0f4a"
+ },
+ "originalModel": {
+ "path": ".local/nebula-lab/compiler/5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9/field.json",
+ "sha256": "2f688583e71134410d5362c10068a7e51dc90db03b284592bdd28207f8a9bc94"
+ },
+ "originalMethod": {
+ "path": ".local/nebula-lab/compiler/5d934218dcc59475e632d503bea59dfeb3bb156431d8d88d3616830808b23df9/method.json",
+ "sha256": "27ce9c6cc38b7e33aeccccd31640aa8a073c9a6bf67ed7757f13bddad15797d5"
+ },
+ "implementation": [
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/application/star-sprites.ts",
+ "sha256": "c1e028918e7006e4a975570fc44f3e54ebf1e254132448f8bae91462ac8f4eda"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/preparation/css-volume.ts",
+ "sha256": "ff2e403521e695afd4b1ced22b177e888c8220024e30c4a8ff3117fe2c4a3616"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/adapters/renderer/volume-validation.ts",
+ "sha256": "d91593c0401885cc46fa28f83b3895c282d6575ab76f062d8ca7b849e761c221"
+ },
+ {
+ "path": "labs/nebula/packages/lab/src/server/workflows/compiler/bake.ts",
+ "sha256": "1b7a185027c89f8f3bd529e6c791eec551b047881ed2995d468b313b6323da12"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/package.json",
+ "sha256": "efc05f933727bd17e5cc84a3a3e2d7f94c3d7da66f7571715eaf8e2df63e8c3d"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compact-inputs/density-grid.ts",
+ "sha256": "3c76d8505d9937ca57a68c03f49b2d7dc368171890a52762e0d005837136dd1a"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/compiler/bake.ts",
+ "sha256": "004bbf282306ea47aa37abed065f5118663d4de18783c51b5ecaa6c3aea24579"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/slices/emission.ts",
+ "sha256": "febe3fc0821a0ca82a4c04d19c0771744cec20fa078bb75a82de3fef043869d7"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/slices/material.ts",
+ "sha256": "dcb0c1b4524eb77ed9547a369ce5a24ede9e298af1ed507e3cfb1e504f5aff96"
+ },
+ {
+ "path": "labs/nebula/packages/volume-bake/src/slices/raster.ts",
+ "sha256": "e10a2774a3613b9700a3f7d8212f24f73438e5df798a6af23f94d102319051d4"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/package.json",
+ "sha256": "767f04fabfceab60608e9ee5fedf637392ee026b3ec5066ff9e59dbdb01a35af"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/compiler-bake.ts",
+ "sha256": "f85da67f5a3ea68a529ae0b449c9a83e991a9b37a926ec54604991a71cd1a4b8"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/render-element-budget.ts",
+ "sha256": "ebde15faebea147d751ae75cfe62be549e1b81392a0982915dea8efc982cdc58"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/contracts/volume-slices.ts",
+ "sha256": "5965a3ac2d6acef6d13ea625df2456960b9197fd4dcfccf7ea8b5b2ec452fda3"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/coordinates/compiler-frame.ts",
+ "sha256": "e340a3eb8f6a374556aa4277cb3ee2064a1ebedd906a012e94f7921d2f4b4d3b"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/materials/cloud-appearance.ts",
+ "sha256": "702cde2ff38ccce2096eb3562681f0ab8c8a9dec424ef8195c84034f4a856930"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/materials/slab-material.ts",
+ "sha256": "f5aebe26a52df5e7fde3fbf404e2505d30f990cfaa4e0a2ecbfcdac0cdfbcda1"
+ },
+ {
+ "path": "labs/nebula/packages/volume-core/src/sampling/layer-optimization.ts",
+ "sha256": "98c9ac63b39b4f1e8ff2fab6b3d6beb5423033fa4a5a9fe5048b54c862b4918b"
+ },
+ {
+ "path": "src/preparation/stars/config.ts",
+ "sha256": "e496a8ec1be2377bfaa536324e8483a8c5a81c41f8bb0678dba30756e4f25209"
+ },
+ {
+ "path": "src/renderers/css/preparation/leaf-bounds.ts",
+ "sha256": "7f090519561a4d13dcecd87e523490429cfbc5aff8f8b902e51019d98db8569c"
+ },
+ {
+ "path": "src/renderers/css/preparation/stars/material.ts",
+ "sha256": "d99ae7dfa75204fb1410f17578bfd0e1b3fa7aa3fd49c13008323f38656990ab"
+ },
+ {
+ "path": "src/renderers/css/preparation/volume-order.ts",
+ "sha256": "491bc4d90a13f23cd5a9d60c55416fef63e5960cdbe33a286777b9438b9f19ea"
+ },
+ {
+ "path": "src/renderers/css/preparation/volume.ts",
+ "sha256": "bc32629f7a0a2e54b8064ccc3285e09a5520fa27b76df4a2e3d3d70ca1d5326b"
+ },
+ {
+ "path": "src/renderers/css/rendering/prepared-leaf-frustum.ts",
+ "sha256": "f38d4e0ad7127c7007e1944318bdd5df18afa6838e5dc1c9bb990f7b208b63bd"
+ },
+ {
+ "path": "src/renderers/css/sky/validation.ts",
+ "sha256": "894958cad5cbafb039bd7393a096c4b28f383dfa73f3fc19f7cb10e21cda4b6b"
+ },
+ {
+ "path": "src/renderers/css/volume/compiler-render-budget.ts",
+ "sha256": "68acdb48f890c5624a819a5a215d242284c8e9603d61aa8a5ca75306015c7b63"
+ },
+ {
+ "path": "src/renderers/css/volume/validation.ts",
+ "sha256": "63a930d5e9b1e6f314204bd35a295bee950cb8859209526b5f5c9632b9917f65"
+ },
+ {
+ "path": "src/renderers/css/volume/volume-impostor-validation.ts",
+ "sha256": "50801a940a417e364824c7b31d329614732cef34dc92f94943c38c5cb711b996"
+ },
+ {
+ "path": "tools/nebula/application/star-sprites.ts",
+ "sha256": "66306b28248a9a19dab50dc42717a7a6b10996cc1c305834eeeffb17412a6f59"
+ }
+ ],
+ "renderProfile": {
+ "schema": "cssearth-render-element-profile@1",
+ "id": "css-volume-single-topology@1",
+ "maximumElements": 500,
+ "elementsPerSlab": 3,
+ "elementsPerStar": 1,
+ "reservedElements": 47
+ },
+ "interpretation": "Only the saved field sampling allocation changes. Scientific field, source registration, material colors and zero separate stars are reused unchanged. Earlier fit metrics describe that unchanged field, not the smaller delivered bank."
+ },
+ "sampling": {
+ "sliceCounts": {
+ "x": 49,
+ "y": 46,
+ "z": 56
+ },
+ "imageWidth": 512,
+ "samplesPerSlab": 4,
+ "layerPlan": {
+ "schema": "cssearth-volume-layer-plan@1",
+ "referenceSliceCounts": {
+ "x": 264,
+ "y": 264,
+ "z": 512
+ },
+ "referenceSamplesPerSlab": 4,
+ "axes": {
+ "x": [
+ {
+ "startCell": 0,
+ "endCell": 39
+ },
+ {
+ "startCell": 39,
+ "endCell": 46
+ },
+ {
+ "startCell": 46,
+ "endCell": 49
+ },
+ {
+ "startCell": 49,
+ "endCell": 56
+ },
+ {
+ "startCell": 56,
+ "endCell": 63
+ },
+ {
+ "startCell": 63,
+ "endCell": 70
+ },
+ {
+ "startCell": 70,
+ "endCell": 75
+ },
+ {
+ "startCell": 75,
+ "endCell": 80
+ },
+ {
+ "startCell": 80,
+ "endCell": 83
+ },
+ {
+ "startCell": 83,
+ "endCell": 86
+ },
+ {
+ "startCell": 86,
+ "endCell": 91
+ },
+ {
+ "startCell": 91,
+ "endCell": 95
+ },
+ {
+ "startCell": 95,
+ "endCell": 98
+ },
+ {
+ "startCell": 98,
+ "endCell": 101
+ },
+ {
+ "startCell": 101,
+ "endCell": 104
+ },
+ {
+ "startCell": 104,
+ "endCell": 107
+ },
+ {
+ "startCell": 107,
+ "endCell": 110
+ },
+ {
+ "startCell": 110,
+ "endCell": 113
+ },
+ {
+ "startCell": 113,
+ "endCell": 115
+ },
+ {
+ "startCell": 115,
+ "endCell": 118
+ },
+ {
+ "startCell": 118,
+ "endCell": 121
+ },
+ {
+ "startCell": 121,
+ "endCell": 124
+ },
+ {
+ "startCell": 124,
+ "endCell": 127
+ },
+ {
+ "startCell": 127,
+ "endCell": 130
+ },
+ {
+ "startCell": 130,
+ "endCell": 133
+ },
+ {
+ "startCell": 133,
+ "endCell": 135
+ },
+ {
+ "startCell": 135,
+ "endCell": 138
+ },
+ {
+ "startCell": 138,
+ "endCell": 141
+ },
+ {
+ "startCell": 141,
+ "endCell": 144
+ },
+ {
+ "startCell": 144,
+ "endCell": 147
+ },
+ {
+ "startCell": 147,
+ "endCell": 150
+ },
+ {
+ "startCell": 150,
+ "endCell": 153
+ },
+ {
+ "startCell": 153,
+ "endCell": 156
+ },
+ {
+ "startCell": 156,
+ "endCell": 159
+ },
+ {
+ "startCell": 159,
+ "endCell": 162
+ },
+ {
+ "startCell": 162,
+ "endCell": 165
+ },
+ {
+ "startCell": 165,
+ "endCell": 168
+ },
+ {
+ "startCell": 168,
+ "endCell": 171
+ },
+ {
+ "startCell": 171,
+ "endCell": 174
+ },
+ {
+ "startCell": 174,
+ "endCell": 178
+ },
+ {
+ "startCell": 178,
+ "endCell": 181
+ },
+ {
+ "startCell": 181,
+ "endCell": 186
+ },
+ {
+ "startCell": 186,
+ "endCell": 193
+ },
+ {
+ "startCell": 193,
+ "endCell": 200
+ },
+ {
+ "startCell": 200,
+ "endCell": 205
+ },
+ {
+ "startCell": 205,
+ "endCell": 211
+ },
+ {
+ "startCell": 211,
+ "endCell": 218
+ },
+ {
+ "startCell": 218,
+ "endCell": 225
+ },
+ {
+ "startCell": 225,
+ "endCell": 264
+ }
+ ],
+ "y": [
+ {
+ "startCell": 0,
+ "endCell": 39
+ },
+ {
+ "startCell": 39,
+ "endCell": 46
+ },
+ {
+ "startCell": 46,
+ "endCell": 51
+ },
+ {
+ "startCell": 51,
+ "endCell": 54
+ },
+ {
+ "startCell": 54,
+ "endCell": 59
+ },
+ {
+ "startCell": 59,
+ "endCell": 66
+ },
+ {
+ "startCell": 66,
+ "endCell": 73
+ },
+ {
+ "startCell": 73,
+ "endCell": 78
+ },
+ {
+ "startCell": 78,
+ "endCell": 83
+ },
+ {
+ "startCell": 83,
+ "endCell": 86
+ },
+ {
+ "startCell": 86,
+ "endCell": 91
+ },
+ {
+ "startCell": 91,
+ "endCell": 96
+ },
+ {
+ "startCell": 96,
+ "endCell": 101
+ },
+ {
+ "startCell": 101,
+ "endCell": 104
+ },
+ {
+ "startCell": 104,
+ "endCell": 107
+ },
+ {
+ "startCell": 107,
+ "endCell": 110
+ },
+ {
+ "startCell": 110,
+ "endCell": 113
+ },
+ {
+ "startCell": 113,
+ "endCell": 116
+ },
+ {
+ "startCell": 116,
+ "endCell": 119
+ },
+ {
+ "startCell": 119,
+ "endCell": 122
+ },
+ {
+ "startCell": 122,
+ "endCell": 125
+ },
+ {
+ "startCell": 125,
+ "endCell": 128
+ },
+ {
+ "startCell": 128,
+ "endCell": 131
+ },
+ {
+ "startCell": 131,
+ "endCell": 134
+ },
+ {
+ "startCell": 134,
+ "endCell": 137
+ },
+ {
+ "startCell": 137,
+ "endCell": 140
+ },
+ {
+ "startCell": 140,
+ "endCell": 143
+ },
+ {
+ "startCell": 143,
+ "endCell": 146
+ },
+ {
+ "startCell": 146,
+ "endCell": 149
+ },
+ {
+ "startCell": 149,
+ "endCell": 152
+ },
+ {
+ "startCell": 152,
+ "endCell": 155
+ },
+ {
+ "startCell": 155,
+ "endCell": 158
+ },
+ {
+ "startCell": 158,
+ "endCell": 161
+ },
+ {
+ "startCell": 161,
+ "endCell": 164
+ },
+ {
+ "startCell": 164,
+ "endCell": 169
+ },
+ {
+ "startCell": 169,
+ "endCell": 172
+ },
+ {
+ "startCell": 172,
+ "endCell": 175
+ },
+ {
+ "startCell": 175,
+ "endCell": 180
+ },
+ {
+ "startCell": 180,
+ "endCell": 187
+ },
+ {
+ "startCell": 187,
+ "endCell": 190
+ },
+ {
+ "startCell": 190,
+ "endCell": 195
+ },
+ {
+ "startCell": 195,
+ "endCell": 202
+ },
+ {
+ "startCell": 202,
+ "endCell": 209
+ },
+ {
+ "startCell": 209,
+ "endCell": 219
+ },
+ {
+ "startCell": 219,
+ "endCell": 228
+ },
+ {
+ "startCell": 228,
+ "endCell": 264
+ }
+ ],
+ "z": [
+ {
+ "startCell": 0,
+ "endCell": 102
+ },
+ {
+ "startCell": 102,
+ "endCell": 105
+ },
+ {
+ "startCell": 105,
+ "endCell": 138
+ },
+ {
+ "startCell": 138,
+ "endCell": 162
+ },
+ {
+ "startCell": 162,
+ "endCell": 177
+ },
+ {
+ "startCell": 177,
+ "endCell": 187
+ },
+ {
+ "startCell": 187,
+ "endCell": 192
+ },
+ {
+ "startCell": 192,
+ "endCell": 201
+ },
+ {
+ "startCell": 201,
+ "endCell": 210
+ },
+ {
+ "startCell": 210,
+ "endCell": 215
+ },
+ {
+ "startCell": 215,
+ "endCell": 219
+ },
+ {
+ "startCell": 219,
+ "endCell": 224
+ },
+ {
+ "startCell": 224,
+ "endCell": 229
+ },
+ {
+ "startCell": 229,
+ "endCell": 232
+ },
+ {
+ "startCell": 232,
+ "endCell": 237
+ },
+ {
+ "startCell": 237,
+ "endCell": 240
+ },
+ {
+ "startCell": 240,
+ "endCell": 243
+ },
+ {
+ "startCell": 243,
+ "endCell": 246
+ },
+ {
+ "startCell": 246,
+ "endCell": 249
+ },
+ {
+ "startCell": 249,
+ "endCell": 252
+ },
+ {
+ "startCell": 252,
+ "endCell": 255
+ },
+ {
+ "startCell": 255,
+ "endCell": 258
+ },
+ {
+ "startCell": 258,
+ "endCell": 261
+ },
+ {
+ "startCell": 261,
+ "endCell": 262
+ },
+ {
+ "startCell": 262,
+ "endCell": 265
+ },
+ {
+ "startCell": 265,
+ "endCell": 268
+ },
+ {
+ "startCell": 268,
+ "endCell": 271
+ },
+ {
+ "startCell": 271,
+ "endCell": 274
+ },
+ {
+ "startCell": 274,
+ "endCell": 275
+ },
+ {
+ "startCell": 275,
+ "endCell": 278
+ },
+ {
+ "startCell": 278,
+ "endCell": 279
+ },
+ {
+ "startCell": 279,
+ "endCell": 281
+ },
+ {
+ "startCell": 281,
+ "endCell": 284
+ },
+ {
+ "startCell": 284,
+ "endCell": 287
+ },
+ {
+ "startCell": 287,
+ "endCell": 290
+ },
+ {
+ "startCell": 290,
+ "endCell": 293
+ },
+ {
+ "startCell": 293,
+ "endCell": 296
+ },
+ {
+ "startCell": 296,
+ "endCell": 299
+ },
+ {
+ "startCell": 299,
+ "endCell": 302
+ },
+ {
+ "startCell": 302,
+ "endCell": 306
+ },
+ {
+ "startCell": 306,
+ "endCell": 309
+ },
+ {
+ "startCell": 309,
+ "endCell": 312
+ },
+ {
+ "startCell": 312,
+ "endCell": 317
+ },
+ {
+ "startCell": 317,
+ "endCell": 322
+ },
+ {
+ "startCell": 322,
+ "endCell": 329
+ },
+ {
+ "startCell": 329,
+ "endCell": 335
+ },
+ {
+ "startCell": 335,
+ "endCell": 342
+ },
+ {
+ "startCell": 342,
+ "endCell": 353
+ },
+ {
+ "startCell": 353,
+ "endCell": 361
+ },
+ {
+ "startCell": 361,
+ "endCell": 368
+ },
+ {
+ "startCell": 368,
+ "endCell": 381
+ },
+ {
+ "startCell": 381,
+ "endCell": 384
+ },
+ {
+ "startCell": 384,
+ "endCell": 405
+ },
+ {
+ "startCell": 405,
+ "endCell": 424
+ },
+ {
+ "startCell": 424,
+ "endCell": 473
+ },
+ {
+ "startCell": 473,
+ "endCell": 512
+ }
+ ]
+ }
+ },
+ "layerOptimization": {
+ "schema": "cssearth-layer-optimization@1",
+ "method": "gradient-weighted-depth-displacement@1",
+ "maximumLayers": 151,
+ "referenceLayers": 1040,
+ "plannedLayers": 151,
+ "probeWidth": 64,
+ "probeSubpixels": 2,
+ "targetNormalizedL1": 0.01,
+ "estimatedNormalizedL1": {
+ "x": 0.1065752005761363,
+ "y": 0.1046658764904314,
+ "z": 0.10729931793886195
+ },
+ "status": "budget-limited"
+ },
+ "renderBudget": {
+ "schema": "cssearth-render-element-budget@1",
+ "profile": {
+ "schema": "cssearth-render-element-profile@1",
+ "id": "css-volume-single-topology@1",
+ "maximumElements": 500,
+ "elementsPerSlab": 3,
+ "elementsPerStar": 1,
+ "reservedElements": 47
+ },
+ "starCount": 0,
+ "slabCount": 151,
+ "maximumSlabs": 151,
+ "totalElements": 500
+ }
+ },
+ "banks": [
+ {
+ "id": "neutral",
+ "counts": [
+ {
+ "axis": "x",
+ "retained": 48
+ },
+ {
+ "axis": "y",
+ "retained": 45
+ },
+ {
+ "axis": "z",
+ "retained": 56
+ }
+ ],
+ "resources": 149,
+ "bytes": 2668542
+ },
+ {
+ "id": "eso1119b",
+ "counts": [
+ {
+ "axis": "x",
+ "retained": 48
+ },
+ {
+ "axis": "y",
+ "retained": 45
+ },
+ {
+ "axis": "z",
+ "retained": 56
+ }
+ ],
+ "resources": 149,
+ "bytes": 7347226
+ },
+ {
+ "id": "eso0844a",
+ "counts": [
+ {
+ "axis": "x",
+ "retained": 48
+ },
+ {
+ "axis": "y",
+ "retained": 45
+ },
+ {
+ "axis": "z",
+ "retained": 56
+ }
+ ],
+ "resources": 149,
+ "bytes": 6476494
+ }
+ ],
+ "compact": {
+ "path": "output/pr390-audit/element-budget/bake-inputs.json.gz",
+ "sha256": "c9f2535b13f4b4745179ec53b63240df1207ef7f38a4ea34dff76cba50a74d68",
+ "bytes": 2229420
+ }
+ },
+ "actualElements": {
+ "limit": 500,
+ "application": [
+ {
+ "stage": "cold arrival",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "warm reselection",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "orbit",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "close zoom",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "return from galaxy context",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "lens eso1119b",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ },
+ {
+ "stage": "lens eso0844a",
+ "elements": 493,
+ "planes": 447,
+ "stars": 0
+ }
+ ],
+ "lab": [
+ {
+ "stage": "initial",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso1119b",
+ "material": "textured",
+ "pose": "0,0",
+ "stars": "true"
+ },
+ {
+ "stage": "lens eso1119b",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso1119b",
+ "material": "textured",
+ "pose": "0,0",
+ "stars": "true"
+ },
+ {
+ "stage": "lens eso0844a",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "0,0",
+ "stars": "true"
+ },
+ {
+ "stage": "before orbit",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "0,0",
+ "stars": "true"
+ },
+ {
+ "stage": "after orbit",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "35,-17.5",
+ "stars": "true"
+ },
+ {
+ "stage": "zoom",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "35,-17.5",
+ "stars": "true"
+ },
+ {
+ "stage": "refresh retained",
+ "elements": 462,
+ "planes": 447,
+ "lens": "eso0844a",
+ "material": "textured",
+ "pose": "35,-17.5",
+ "stars": "true"
+ }
+ ],
+ "baselineApplication": 1423,
+ "reductionFraction": 0.6535488404778637
+ },
+ "checks": [
+ {
+ "command": "pnpm build",
+ "result": "passed",
+ "builtPages": 995
+ },
+ {
+ "command": "pnpm test:renderer",
+ "result": "passed",
+ "tests": 596
+ },
+ {
+ "command": "pnpm typecheck",
+ "result": "passed"
+ },
+ {
+ "command": "pnpm typecheck:tests",
+ "result": "passed",
+ "files": 1759
+ },
+ {
+ "command": "pnpm check:typescript-ownership",
+ "result": "passed"
+ },
+ {
+ "command": "Exact maintained nebula/source CI command",
+ "result": "passed",
+ "outerTests": 22,
+ "typedGuardCases": 9,
+ "typedInstallerCases": 6,
+ "receipt": "delivery-guard.json"
+ },
+ {
+ "command": "Source/provenance/object-package tests",
+ "result": "passed",
+ "tests": 29
+ },
+ {
+ "command": "Targeted compiler/core/compact/component tests",
+ "result": "passed",
+ "tests": 31,
+ "receipt": "implementation.json"
+ },
+ {
+ "command": "node labs/nebula/run.mts test",
+ "result": "failed",
+ "tests": 686,
+ "passed": 664,
+ "skipped": 17,
+ "failed": 5,
+ "receipt": "lab-suite.tap",
+ "limits": "Four missing LMC research assets and one temporary fixture package-resolution failure. Same failure categories as prior recorded run; not independently reproduced on current main. No native LMC setup or rerun claimed."
+ },
+ {
+ "command": "Actual Lab browser",
+ "result": "passed",
+ "receipt": "lab-browser.json"
+ },
+ {
+ "command": "Actual native app browser",
+ "result": "passed",
+ "receipt": "native-focus.json"
+ },
+ {
+ "command": "Controlled main-app views",
+ "result": "passed",
+ "captures": 7,
+ "receipt": "app-inspection.json"
+ },
+ {
+ "command": "Isolated compact application replay",
+ "result": "passed",
+ "seconds": 232.932,
+ "receipt": "cold-replay.json"
+ },
+ {
+ "command": "R2 cold restore and network-forbidden warm reuse",
+ "result": "passed",
+ "files": 64,
+ "receipt": "r2-restore.json"
+ },
+ {
+ "command": "Cross-vendor scoped review and mutations",
+ "result": "passed",
+ "receipt": "review/verified.md"
+ }
+ ],
+ "logs": [
+ {
+ "path": "output/pr390-audit/element-budget/renderer-tests.log",
+ "bytes": 8087,
+ "sha256": "aeb58bd5a8bc023cfc7473c9792137fb6f8821f7b1967b72f0917eae5000b4d4"
+ },
+ {
+ "path": "output/pr390-audit/element-budget/typecheck-final.log",
+ "bytes": 7774,
+ "sha256": "669f4b7c8a8a2deb84b026942ad985f87fa6e888bc37d5bb7cc33975cf3c665e"
+ },
+ {
+ "path": "output/pr390-audit/element-budget/test-types-final.log",
+ "bytes": 162,
+ "sha256": "167f55fe51b2568f30e8c9b04735774271794eb932652bbac319e4778ec1f187"
+ },
+ {
+ "path": "output/pr390-audit/element-budget/ownership-final.log",
+ "bytes": 287,
+ "sha256": "3876711c2a3211867d7e33acda2459d45f6d8245ddc42560113231183956cf9e"
+ },
+ {
+ "path": "output/pr390-audit/element-budget/provenance-tests.log",
+ "bytes": 6880,
+ "sha256": "28adfa5567956f72fde8a9cdbf53e136e6648dbb02cd8b158f7d3b344916140e"
+ },
+ {
+ "path": "output/pr390-audit/element-budget/exact-ci-tests-final.log",
+ "bytes": 9426,
+ "sha256": "360b2ca048c447d238b71cf23216dcabd9fb56a83c7ba597bb3c052c5576b36e"
+ },
+ {
+ "path": "output/pr390-audit/element-budget/typecheck-stable.log",
+ "bytes": 0,
+ "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ },
+ {
+ "path": "output/pr390-audit/element-budget/build.log",
+ "bytes": 1007739,
+ "sha256": "bce6de47350b79708c1ce635176755a87a5104d85730479ee5931c7cf8dafbf4"
+ }
+ ],
+ "publication": {
+ "inventoryPins": {
+ "object.json": "e709e55c5ec0ad4f4846303cffec0d6c34c235554a69e5a8481535e9465e4255",
+ "runtime-assets.json": "e30940cfcec240029ada2ddd666379804e57d863087d97a7ad91be598cb0f5bf",
+ "prepared-assets.json": "ac43536da6c0b558e221253a673b14c30dcecd61d5a504c38936ef307351a448"
+ }
+ },
+ "visual": {
+ "status": "failed",
+ "handoffs": "handoffs.json",
+ "matchedFronts": "pixelmatch.json",
+ "interpretation": "Reducing retained nodes improves the element allocation, not scientific or visual qualification. All nine handoffs exceed normalizedL1 0.04; smaller-bank front brightness is23-25% higher. No tolerance was loosened and no physical field or star positions were refitted."
+ },
+ "validationRecovery": "Early native checks failed while metadata pins were being refreshed; later attempts collided with development-module rebuilds and were cancelled. Final browser passes above ran against a restarted task preview after the completed build. They are not passes from a merely started process.",
+ "mergeStatus": "draft; visual qualification remains blocked"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-earth.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-earth.png
new file mode 100644
index 0000000000..13cfe0a497
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-earth.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-oblique.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-oblique.png
new file mode 100644
index 0000000000..af0232c64c
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-oblique.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-minus.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-minus.png
new file mode 100644
index 0000000000..53e3a4b563
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-minus.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-plus.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-plus.png
new file mode 100644
index 0000000000..8446f6346a
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-plus.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-y.png b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-y.png
new file mode 100644
index 0000000000..f528cf124e
Binary files /dev/null and b/src/objects/omega-centauri/evidence/2026-09-21/element-budget/view-side-y.png differ
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/fixture-placement.json b/src/objects/omega-centauri/evidence/2026-09-21/fixture-placement.json
new file mode 100644
index 0000000000..5ddd2e6d2e
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/fixture-placement.json
@@ -0,0 +1,1945 @@
+{
+ "schema": "cssearth-fixture-placement-repair@1",
+ "testedBase": "7a4f38985d98c4c03e7c48710428d9017f45f9d5",
+ "reason": "Contract lint rejects 44 raw fixture files in docs plus an unreachable guide; five downstream CI checks stopped before running.",
+ "repair": {
+ "round": 2,
+ "maximumCleanupRounds": 2,
+ "base": "7a4f38985d98c4c03e7c48710428d9017f45f9d5",
+ "lintBase": "3aebc31094dff8332755b394b88c708b7c3f1062",
+ "counts": {
+ "movedFiles": 44,
+ "byteIdenticalArtifacts": 18,
+ "byteIdenticalParameters": 8,
+ "descriptors": 17,
+ "descriptorMembers": 22,
+ "relativeReferenceFields": 34,
+ "sourceEntriesVerified": 18,
+ "notIgnoredEntries": 44
+ },
+ "files": [
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F01.csv",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F01.csv",
+ "bytes": 320309,
+ "sha256": "511587d2174225fee2ea4eaf7050bd580275dc45ef83951144539bc4c61fa64c",
+ "afterBytes": 320309,
+ "afterSha256": "511587d2174225fee2ea4eaf7050bd580275dc45ef83951144539bc4c61fa64c",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F02.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F02.json",
+ "bytes": 2462,
+ "sha256": "7d0df1bc4589ac6ca0c7c1389485ab8dcd4e3bb20c5b79b825e93d36f30dce1d",
+ "afterBytes": 2462,
+ "afterSha256": "7d0df1bc4589ac6ca0c7c1389485ab8dcd4e3bb20c5b79b825e93d36f30dce1d",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F03.csv",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F03.csv",
+ "bytes": 10007,
+ "sha256": "d6aaea2ee5c61624057ad502bf392bdc6871acf832e5b35a93608464b4988c76",
+ "afterBytes": 10007,
+ "afterSha256": "d6aaea2ee5c61624057ad502bf392bdc6871acf832e5b35a93608464b4988c76",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F04.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F04.json",
+ "bytes": 2739,
+ "sha256": "10118793a5f980fe81a9361b100ad3f4fe1a429c52152ed383e36e043f957ae2",
+ "afterBytes": 2739,
+ "afterSha256": "10118793a5f980fe81a9361b100ad3f4fe1a429c52152ed383e36e043f957ae2",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F05.csv",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F05.csv",
+ "bytes": 151,
+ "sha256": "69f48cfa89a61b1d919592adaec48c0f8f185a46900030462f1198580884152f",
+ "afterBytes": 151,
+ "afterSha256": "69f48cfa89a61b1d919592adaec48c0f8f185a46900030462f1198580884152f",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F06.csv",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F06.csv",
+ "bytes": 343,
+ "sha256": "42fecf1d339745021f7aa337f751b5e8339e4ad5ba1abd9808fc17fb7ce9d0c9",
+ "afterBytes": 343,
+ "afterSha256": "42fecf1d339745021f7aa337f751b5e8339e4ad5ba1abd9808fc17fb7ce9d0c9",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F07.csv",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F07.csv",
+ "bytes": 354,
+ "sha256": "a6724d8ec1746ff26583e1545b0d2f6b1ec04d5cdcd8250225634db5417aa291",
+ "afterBytes": 354,
+ "afterSha256": "a6724d8ec1746ff26583e1545b0d2f6b1ec04d5cdcd8250225634db5417aa291",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F08.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F08.json",
+ "bytes": 3227,
+ "sha256": "1f4270ec5ad4467645f043486a789bf47e055ab9599a2ad0b984626ed63fdde2",
+ "afterBytes": 3227,
+ "afterSha256": "1f4270ec5ad4467645f043486a789bf47e055ab9599a2ad0b984626ed63fdde2",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F09.csv",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F09.csv",
+ "bytes": 30677,
+ "sha256": "b41b12fbd1470980a5f9e2676185d6d7bc52eadb26ba9d7b716fc5e916dfcd72",
+ "afterBytes": 30677,
+ "afterSha256": "b41b12fbd1470980a5f9e2676185d6d7bc52eadb26ba9d7b716fc5e916dfcd72",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F10.csv",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F10.csv",
+ "bytes": 1234502,
+ "sha256": "008a06a057b541851b44aa8c9fc823f01954e395d488bf54d6d04f0c6c47d0dc",
+ "afterBytes": 1234502,
+ "afterSha256": "008a06a057b541851b44aa8c9fc823f01954e395d488bf54d6d04f0c6c47d0dc",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F11.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F11.json",
+ "bytes": 1816,
+ "sha256": "9324e9afeb0ed225b6279c6b99e135cfad0938caaa22b7be9a926a8d2d8e8536",
+ "afterBytes": 1816,
+ "afterSha256": "9324e9afeb0ed225b6279c6b99e135cfad0938caaa22b7be9a926a8d2d8e8536",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F12.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F12.json",
+ "bytes": 153585,
+ "sha256": "fe0e04f589f650ba5ca105f959863493f969e5f0bc07d79b174c97cf47500c22",
+ "afterBytes": 153585,
+ "afterSha256": "fe0e04f589f650ba5ca105f959863493f969e5f0bc07d79b174c97cf47500c22",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F13.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F13.json",
+ "bytes": 537,
+ "sha256": "b162ce03379dfe3a287994904fc4bac682a8e511e810b9da2eb5d555c3e109d9",
+ "afterBytes": 537,
+ "afterSha256": "b162ce03379dfe3a287994904fc4bac682a8e511e810b9da2eb5d555c3e109d9",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F14.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F14.json",
+ "bytes": 528,
+ "sha256": "d13871b14d55804e8cd88da19608ba6a60babc3abffff82b370c9d8dc697a65f",
+ "afterBytes": 528,
+ "afterSha256": "d13871b14d55804e8cd88da19608ba6a60babc3abffff82b370c9d8dc697a65f",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F15.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F15.json",
+ "bytes": 235,
+ "sha256": "f9f23442978f2f328d758eda7f24f5501e5d739ff3d0e3222f9e9676213774f3",
+ "afterBytes": 235,
+ "afterSha256": "f9f23442978f2f328d758eda7f24f5501e5d739ff3d0e3222f9e9676213774f3",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F16.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F16.json",
+ "bytes": 399,
+ "sha256": "ab61e17f55f19c6cdd90d189f7c85c2b8ec714f7065b448759bffad699ccd534",
+ "afterBytes": 399,
+ "afterSha256": "ab61e17f55f19c6cdd90d189f7c85c2b8ec714f7065b448759bffad699ccd534",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F17.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F17.json",
+ "bytes": 850,
+ "sha256": "3ce0da2fb9110c5d78d52bf10f96a534a4b1e1d1cda920e2b0712af6b87c01ef",
+ "afterBytes": 850,
+ "afterSha256": "3ce0da2fb9110c5d78d52bf10f96a534a4b1e1d1cda920e2b0712af6b87c01ef",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/artifacts/F18.json",
+ "newPath": "tests/fixtures/telescope-family-examples/artifacts/F18.json",
+ "bytes": 1150,
+ "sha256": "850afec6860bc1c07ca50f5da408fcf3f50832f61ee54c8ee351844e5119be13",
+ "afterBytes": 1150,
+ "afterSha256": "850afec6860bc1c07ca50f5da408fcf3f50832f61ee54c8ee351844e5119be13",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F02.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F02.json",
+ "bytes": 3289,
+ "sha256": "62fcd738e49d7e17d879a606ec5a4bea036ba5e2917acd6d9a65e5f232f97f60",
+ "afterBytes": 3271,
+ "afterSha256": "420084ef3535cc83d790ef5652ce4b17fcf10dae431de935d6e6a17b29b94866",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F03.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F03.json",
+ "bytes": 2924,
+ "sha256": "d7c54fa12a84bb6f3619c9393c7f58a5e41cc8e7ae2e04a18993b88443ac61ed",
+ "afterBytes": 2930,
+ "afterSha256": "b3715ae7dc141dbee9b3a099db71de9a34fdd549c02c53ee399e85eb481adfd2",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F04.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F04.json",
+ "bytes": 4677,
+ "sha256": "fa10e0795c6465aa77d802c810a3b07a10e261c247066374b5528ce54ed902ad",
+ "afterBytes": 4686,
+ "afterSha256": "2a0b2eba1c17c24f2e6e915b3509c182a0b17286652805b118a0f34dfff584b5",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F05.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F05.json",
+ "bytes": 3645,
+ "sha256": "ab977b771cc7132d6872bbb8a04d125fa71b65555088db01fcfe7ca7a6c715c2",
+ "afterBytes": 3651,
+ "afterSha256": "30fe57d5cac0b04073eff07e9ec798191fd6ef8ff1bf8dee13e38826219ca0b9",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F06.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F06.json",
+ "bytes": 3289,
+ "sha256": "ce1ad9cfe37228886bbda9dca735ceb4032f729851b9cbfce95652cd82bc9f2d",
+ "afterBytes": 3295,
+ "afterSha256": "eddf325936f716b7715f46f2c491e724bd2833e1046db2a5c6bc6bd27add8f5f",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F07.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F07.json",
+ "bytes": 2778,
+ "sha256": "c1ea1aa867b4dd9634bd6d86bc144ffe14daf3023dbd52f2aa19812a4c57b99e",
+ "afterBytes": 2760,
+ "afterSha256": "8df95c6e1e292a20da0c07481d1e03cdafe442c25c1141f9378e1d263a2b88c3",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F08.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F08.json",
+ "bytes": 6636,
+ "sha256": "2a4769accf40d6f4e5163bf8f5abeb1a918caf5cbb9ebbfa4caab9c4cd2158b1",
+ "afterBytes": 6642,
+ "afterSha256": "f22adfc88e14d293d0f7b4f313a3dccbbd288598f1281542add0f34955d69497",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F09.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F09.json",
+ "bytes": 4310,
+ "sha256": "6204a72d2c4b93b31a354854af189a8b5ba96cb33ebd415da5e872f6c5e0dae3",
+ "afterBytes": 4316,
+ "afterSha256": "02768efe33be9974fc7fbc8cfdd1bc9c51e3f3e272c86bad62fd2e0920e06767",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F10.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F10.json",
+ "bytes": 4715,
+ "sha256": "023170a87411ca8c40b7ca27b5442a22936cc9928cf39d2f0f3d9a7c0489daa4",
+ "afterBytes": 4679,
+ "afterSha256": "0c00ebd8697782fa1b549881c1efadd8ca5dd1bab0278dc2c9293275137cfc24",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F11.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F11.json",
+ "bytes": 2705,
+ "sha256": "6e2ac2b409919a0ca9e050d002864dd99f8ff64a44786252d9eb8b5a69752d29",
+ "afterBytes": 2669,
+ "afterSha256": "432abbf3380bc744907b46e3a3c5dcb2aec18a62287a96dffaf9fecaf1410453",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F12.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F12.json",
+ "bytes": 3991,
+ "sha256": "2a41d16eb5250a976be591d8c1915d2bf438e8fc1c9188281dc19b861c5b8de8",
+ "afterBytes": 3997,
+ "afterSha256": "7a1e9e8bb63a9e35a8fcbf55532b41347cb77d9143c60b8c91d1c3c7d05e91f8",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F13.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F13.json",
+ "bytes": 10231,
+ "sha256": "10cf2dfaef3eb978ca4e3edd4316bae809ea998f98f3557d36849894aba3ba26",
+ "afterBytes": 10195,
+ "afterSha256": "928e541713af255fe8b2b3dc2a7af3030fbcea9768eda8bf81901d4b1666bc93",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F14.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F14.json",
+ "bytes": 2363,
+ "sha256": "94aa9668b1f410840391cf726a2894790b5a022041bed91bc7506e78fb296981",
+ "afterBytes": 2345,
+ "afterSha256": "f7156e3b8ca6ace3d02785da72ba9f4a717f3f27f66ec87b19d10fd7976b09b9",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F15.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F15.json",
+ "bytes": 3405,
+ "sha256": "5794716a87fe7f9144f579f578c46e24ffc6a32122111f34204004f066057ed9",
+ "afterBytes": 3369,
+ "afterSha256": "7790e595e617e7330f6b204cee88b7a68a3aab403b420ca2749bb9733e761aeb",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F16.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F16.json",
+ "bytes": 5092,
+ "sha256": "d7b8449d3f5901de7dc5bbdfb1de54d0b8c44815512aca016cf578acc7cd7037",
+ "afterBytes": 5095,
+ "afterSha256": "45d1ed9e1a1641727d558e58ccaf304d4bb61373d90ef6e134ea3d26d7923584",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F17.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F17.json",
+ "bytes": 3618,
+ "sha256": "d51419422be27489a0e4d489aa7051fda0afeec6c9d60b54bc8bc31c38e79961",
+ "afterBytes": 3564,
+ "afterSha256": "d464887ddb725ae357976be42d7d30e05068a352001b5c3326db075d68eba893",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/descriptors/F18.json",
+ "newPath": "tests/fixtures/telescope-family-examples/descriptors/F18.json",
+ "bytes": 4219,
+ "sha256": "b4e12c081d0bd5695d86e4a474eafdda37bf3f04e408c0de00247614a4c42539",
+ "afterBytes": 4231,
+ "afterSha256": "4617e78eaa8e4ec241f6e4be79e0eb20b67747df90444d42d8e685ea778d862a",
+ "claim": "Only relative references changed; resolved targets and all other values identical."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/manifest.json",
+ "newPath": "tests/fixtures/telescope-family-examples/manifest.json",
+ "bytes": 25150,
+ "sha256": "f6f58874fcc709df139cae1fe72eb1d6945e102cabd76e793e4a564967bc5fc3",
+ "afterBytes": 25400,
+ "afterSha256": "9f06d9f09d17f3db7ba99d29ba2bdb56f27935299c83772862a81a87f7f776f3",
+ "claim": "Only command descriptor and parameter prefixes changed."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/params/F02.json",
+ "newPath": "tests/fixtures/telescope-family-examples/params/F02.json",
+ "bytes": 106,
+ "sha256": "f3d2946202e381939ecd0dddd78fc491cc3feb5e50b2ad269bff5364733fc060",
+ "afterBytes": 106,
+ "afterSha256": "f3d2946202e381939ecd0dddd78fc491cc3feb5e50b2ad269bff5364733fc060",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/params/F06.json",
+ "newPath": "tests/fixtures/telescope-family-examples/params/F06.json",
+ "bytes": 105,
+ "sha256": "2868a5ba16f74eaee6137229c60c557587d2d74b6e5df73837340588160705bb",
+ "afterBytes": 105,
+ "afterSha256": "2868a5ba16f74eaee6137229c60c557587d2d74b6e5df73837340588160705bb",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/params/F07.json",
+ "newPath": "tests/fixtures/telescope-family-examples/params/F07.json",
+ "bytes": 96,
+ "sha256": "0dc96be0ededa57992a6ebf255d70a8add9feaf93cb03899fdb0b389c7cb83d4",
+ "afterBytes": 96,
+ "afterSha256": "0dc96be0ededa57992a6ebf255d70a8add9feaf93cb03899fdb0b389c7cb83d4",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/params/F10.json",
+ "newPath": "tests/fixtures/telescope-family-examples/params/F10.json",
+ "bytes": 124,
+ "sha256": "cf61276bebfd465b6e01d4a8a01757723ec2e81c5c158043c3c2a4bc74959479",
+ "afterBytes": 124,
+ "afterSha256": "cf61276bebfd465b6e01d4a8a01757723ec2e81c5c158043c3c2a4bc74959479",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/params/F11.json",
+ "newPath": "tests/fixtures/telescope-family-examples/params/F11.json",
+ "bytes": 325,
+ "sha256": "ca840fa313c16c9c4606c7efb849108b329dda625605567935384588ff88ae23",
+ "afterBytes": 325,
+ "afterSha256": "ca840fa313c16c9c4606c7efb849108b329dda625605567935384588ff88ae23",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/params/F13.json",
+ "newPath": "tests/fixtures/telescope-family-examples/params/F13.json",
+ "bytes": 130,
+ "sha256": "20cf4eb66ed978177f99a91200d89fe526314d6bd352159fa1e1b442db1f5e0e",
+ "afterBytes": 130,
+ "afterSha256": "20cf4eb66ed978177f99a91200d89fe526314d6bd352159fa1e1b442db1f5e0e",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/params/F14.json",
+ "newPath": "tests/fixtures/telescope-family-examples/params/F14.json",
+ "bytes": 117,
+ "sha256": "9ac35bc28dcd79c762da518096141fc3ba4464ce8a74121de873145ab6a69cdf",
+ "afterBytes": 117,
+ "afterSha256": "9ac35bc28dcd79c762da518096141fc3ba4464ce8a74121de873145ab6a69cdf",
+ "claim": "Byte-identical relocation."
+ },
+ {
+ "oldPath": "docs/telescope-family-examples/params/F15.json",
+ "newPath": "tests/fixtures/telescope-family-examples/params/F15.json",
+ "bytes": 83,
+ "sha256": "dea1e9d07e8f940b01a90914bd82a0f0e8decb4b3667eb109af7ffc658ecce7d",
+ "afterBytes": 83,
+ "afterSha256": "dea1e9d07e8f940b01a90914bd82a0f0e8decb4b3667eb109af7ffc658ecce7d",
+ "claim": "Byte-identical relocation."
+ }
+ ],
+ "references": [
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F02.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "newValue": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "target": "tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F03.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
+ "newValue": "../../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
+ "target": "src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F03.json",
+ "field": "members.0.path",
+ "oldValue": "../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
+ "newValue": "../../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
+ "target": "src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F04.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../tools/objects/hst/programs/europa-salt-map.scan.json",
+ "newValue": "../../../../tools/objects/hst/programs/europa-salt-map.scan.json",
+ "target": "tools/objects/hst/programs/europa-salt-map.scan.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F04.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tools/objects/hst/programs/europa-salt-map.scan.json",
+ "newValue": "../../../../tools/objects/hst/programs/europa-salt-map.scan.json",
+ "target": "tools/objects/hst/programs/europa-salt-map.scan.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F04.json",
+ "field": "members.1.path",
+ "oldValue": "../../../tools/objects/hst/programs/europa-salt-map.scan.reproduction.json",
+ "newValue": "../../../../tools/objects/hst/programs/europa-salt-map.scan.reproduction.json",
+ "target": "tools/objects/hst/programs/europa-salt-map.scan.reproduction.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F05.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../src/objects/haumea/source/photometry/disc-color.json",
+ "newValue": "../../../../src/objects/haumea/source/photometry/disc-color.json",
+ "target": "src/objects/haumea/source/photometry/disc-color.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F05.json",
+ "field": "members.0.path",
+ "oldValue": "../../../src/objects/haumea/source/photometry/disc-color.json",
+ "newValue": "../../../../src/objects/haumea/source/photometry/disc-color.json",
+ "target": "src/objects/haumea/source/photometry/disc-color.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F06.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "newValue": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "target": "src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F06.json",
+ "field": "members.0.path",
+ "oldValue": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "newValue": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "target": "src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F07.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/wind-rad1/wi_l2_wav_rad1_20200101_v01.cdf",
+ "newValue": "../../telescope-families/wind-rad1/wi_l2_wav_rad1_20200101_v01.cdf",
+ "target": "tests/fixtures/telescope-families/wind-rad1/wi_l2_wav_rad1_20200101_v01.cdf"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F08.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "newValue": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "target": "src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F08.json",
+ "field": "members.0.path",
+ "oldValue": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "newValue": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "target": "src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F09.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../src/objects/m1/source/stellar-field.json",
+ "newValue": "../../../../src/objects/m1/source/stellar-field.json",
+ "target": "src/objects/m1/source/stellar-field.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F09.json",
+ "field": "members.0.path",
+ "oldValue": "../../../src/objects/m1/source/stellar-field.json",
+ "newValue": "../../../../src/objects/m1/source/stellar-field.json",
+ "target": "src/objects/m1/source/stellar-field.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F10.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
+ "newValue": "../../telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
+ "target": "tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F10.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
+ "newValue": "../../telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
+ "target": "tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F11.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
+ "newValue": "../../telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
+ "target": "tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F11.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
+ "newValue": "../../telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
+ "target": "tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F12.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
+ "newValue": "../../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
+ "target": "src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F12.json",
+ "field": "members.0.path",
+ "oldValue": "../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
+ "newValue": "../../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
+ "target": "src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F13.json",
+ "field": "dataset.acquisition.identity",
+ "oldValue": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "newValue": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "target": "tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F13.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "newValue": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "target": "tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F14.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/f14-healpix/bayestar.fits.gz",
+ "newValue": "../../telescope-families/f14-healpix/bayestar.fits.gz",
+ "target": "tests/fixtures/telescope-families/f14-healpix/bayestar.fits.gz"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F15.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/geographos-radar/ge007.fit",
+ "newValue": "../../telescope-families/geographos-radar/ge007.fit",
+ "target": "tests/fixtures/telescope-families/geographos-radar/ge007.fit"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F15.json",
+ "field": "members.1.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/geographos-radar/ge007.lbl",
+ "newValue": "../../telescope-families/geographos-radar/ge007.lbl",
+ "target": "tests/fixtures/telescope-families/geographos-radar/ge007.lbl"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F16.json",
+ "field": "members.0.path",
+ "oldValue": "../../../src/objects/stellar-neighbourhood/object.json",
+ "newValue": "../../../../src/objects/stellar-neighbourhood/object.json",
+ "target": "src/objects/stellar-neighbourhood/object.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F17.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p.fit",
+ "newValue": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p.fit",
+ "target": "tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p.fit"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F17.json",
+ "field": "members.1.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.fit",
+ "newValue": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.fit",
+ "target": "tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.fit"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F17.json",
+ "field": "members.2.path",
+ "oldValue": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.xml",
+ "newValue": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.xml",
+ "target": "tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.xml"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F18.json",
+ "field": "members.0.path",
+ "oldValue": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
+ "newValue": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
+ "target": "tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F18.json",
+ "field": "members.1.path",
+ "oldValue": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "newValue": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "target": "tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F18.json",
+ "field": "components.0.name",
+ "oldValue": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
+ "newValue": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
+ "target": "tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json"
+ },
+ {
+ "descriptor": "tests/fixtures/telescope-family-examples/descriptors/F18.json",
+ "field": "components.1.name",
+ "oldValue": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "newValue": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "target": "tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json"
+ }
+ ],
+ "sourceChecks": [
+ {
+ "family": "F01",
+ "path": "src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum-squeeze.fits",
+ "url": "https://doi.org/10.1051/0004-6361/202244693",
+ "bytes": 141120,
+ "sha256": "372ed3278212fc3a51788e2c695b58d35108d91e3fcda80213b8d440af581dd6",
+ "verified": true
+ },
+ {
+ "family": "F02",
+ "path": "tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "url": "https://rm-lite.readthedocs.io/en/stable/examples/rmsynth_3d.html",
+ "bytes": 5760,
+ "sha256": "53c938b025d398986f37c8861a96caa1a988a403635d4aec1a06e3f4b4d92299",
+ "verified": true
+ },
+ {
+ "family": "F03",
+ "path": "src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
+ "url": "https://zenodo.org/records/7433033",
+ "bytes": 13609,
+ "sha256": "b1aaec4f4361318c40d4f4cbc10c798baca0dbfb309cdab41152d6f83925d756",
+ "verified": true
+ },
+ {
+ "family": "F04",
+ "path": "tools/objects/hst/programs/europa-salt-map.scan.reproduction.json",
+ "url": "https://doi.org/10.1126/sciadv.aaw7123",
+ "bytes": 21487,
+ "sha256": "c23af3b17dd98e3bc95e2620e4729390889a986383e0897eee2b8cf885f5f49e",
+ "verified": true
+ },
+ {
+ "family": "F05",
+ "path": "src/objects/haumea/source/photometry/disc-color.json",
+ "url": "https://doi.org/10.1038/s41586-017-1784-8",
+ "bytes": 2084,
+ "sha256": "9537eff943560b6d733cdf0aa7b4d675d75e45a7e1b833cd7e56619f4331debc",
+ "verified": true
+ },
+ {
+ "family": "F06",
+ "path": "src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "url": "https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:TESS/product/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "bytes": 1944000,
+ "sha256": "442c1d97c549019f46b98026a6b74cd380112722e3e230e79203559f67cc326b",
+ "verified": true
+ },
+ {
+ "family": "F07",
+ "path": "tests/fixtures/telescope-families/wind-rad1/wi_l2_wav_rad1_20200101_v01.cdf",
+ "url": "https://spdf.gsfc.nasa.gov/pub/data/wind/waves/rad1_l2/2020/wi_l2_wav_rad1_20200101_v01.cdf",
+ "bytes": 423932,
+ "sha256": "a1fd0d0c2487592cd0a43672f50aede256dcdc2fed8bc4c3a87fb0799b20647b",
+ "verified": true
+ },
+ {
+ "family": "F08",
+ "path": "src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "url": "https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:TESS/product/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "bytes": 1944000,
+ "sha256": "442c1d97c549019f46b98026a6b74cd380112722e3e230e79203559f67cc326b",
+ "verified": true
+ },
+ {
+ "family": "F09",
+ "path": "src/objects/m1/source/stellar-field.json",
+ "url": "https://dc.zah.uni-heidelberg.de/tableinfo/gedr3dist.litewithdist",
+ "bytes": 128434,
+ "sha256": "4b4502d725e6a4df12d46a6aa0339556140a82d8f0dde2df8b32db840ca2d177",
+ "verified": true
+ },
+ {
+ "family": "F10",
+ "path": "tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
+ "url": "https://cda.cfa.harvard.edu/chaser/startViewer.do?menuItem=details&obsid=6431",
+ "bytes": 2486220,
+ "sha256": "14d5d8da4fcf108a7ec6d3d5ee8db31ed7e641ba8f66a52f7671322b928ce0e5",
+ "verified": true
+ },
+ {
+ "family": "F11",
+ "path": "tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
+ "url": "https://github.com/RadioAstronomySoftwareGroup/pyuvdata/tree/main/pyuvdata/data",
+ "bytes": 4253760,
+ "sha256": "926ed6175d6725f6f27b04a97b658d13341f528a3847b5bc1b7246a24bde4b27",
+ "verified": true
+ },
+ {
+ "family": "F12",
+ "path": "src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
+ "url": "https://doi.org/10.1051/0004-6361/202244693",
+ "bytes": 135360,
+ "sha256": "e4f41d2aba17736ff1fca5e819d60eaf3d38a8e784f6fc2998d587b0c682bcbd",
+ "verified": true
+ },
+ {
+ "family": "F13",
+ "path": "tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "url": "https://rm-lite.readthedocs.io/en/stable/examples/rmsynth_3d.html",
+ "bytes": 5760,
+ "sha256": "53c938b025d398986f37c8861a96caa1a988a403635d4aec1a06e3f4b4d92299",
+ "verified": true
+ },
+ {
+ "family": "F14",
+ "path": "tests/fixtures/telescope-families/f14-healpix/bayestar.fits.gz",
+ "url": "https://github.com/astropy/reproject/blob/main/reproject/healpix/tests/data/bayestar.fits.gz",
+ "bytes": 102936,
+ "sha256": "18823330e933185c7bb8df402d1abbf20da7dffe34b2a7b94d171a961d224515",
+ "verified": true
+ },
+ {
+ "family": "F15",
+ "path": "tests/fixtures/telescope-families/geographos-radar/ge007.fit",
+ "url": "https://sbnarchive.psi.edu/pds3/non_mission/EAR_A_GST_3_RDR_GEOGRAPHOS_RADAR_V1_1/data/ge007.fit",
+ "bytes": 72000,
+ "sha256": "e26d04af718cd5f98a2ba0b1f3a9beee509f1ae2168ddec33cab24653a8725a2",
+ "verified": true
+ },
+ {
+ "family": "F16",
+ "path": "src/objects/stellar-neighbourhood/object.json",
+ "url": "https://codeberg.org/astronexus/hyg",
+ "bytes": 906,
+ "sha256": "fa4dbdb551af2ecfa85dc4964218f8708bbab60401f8d452a7a5230776c376e1",
+ "verified": true
+ },
+ {
+ "family": "F17",
+ "path": "tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.fit",
+ "url": "https://sbnarchive.psi.edu/pds4/near/near.msi_v1.0/data_calibrated/mathilde/1997/178/iof/m0042826360f0_2p_iof.fit",
+ "bytes": 535680,
+ "sha256": "c852aeb4858a579821e724419c4cdebfb895749423c039e54e75b72986a8de8d",
+ "verified": true
+ },
+ {
+ "family": "F18",
+ "path": "tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "url": "https://pdssbn.astro.umd.edu/holdings/pds4-nh_derived:plutosystem_composition-v1.0/mosaic/nh_charon_color_mosaic.lblx",
+ "bytes": 4820,
+ "sha256": "ab824d182a6eeb08ac4b69ad1dd90c51f12070f3c5f66579258b645f72df4c47",
+ "verified": true
+ }
+ ],
+ "restoredExistingSource": {
+ "donor": "/Users/apresmoi/Documents/cssEarth/src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "path": "src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "bytes": 1944000,
+ "sha256": "442c1d97c549019f46b98026a6b74cd380112722e3e230e79203559f67cc326b"
+ },
+ "checks": [
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round2-family-examples.log",
+ "bytes": 361,
+ "sha256": "a61ee6924b4b6004154e29304b0032bd2cfcabcc63474dd4c81b04f3d06cd956"
+ },
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round2-documentation.json",
+ "bytes": 477,
+ "sha256": "c06cc8bbfb4e1b9e597f90fd6e76934e80b07d2fef92d4f5f91e66d937c80bf5"
+ },
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round2-acquisition-mutation.log",
+ "bytes": 1029,
+ "sha256": "0d9c8bdd04435f1c86024f4c3faaa09f5abdbee76548cc619ef27ef445e7e6d4"
+ },
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round2-dispatch.log",
+ "bytes": 1493,
+ "sha256": "670f28dbf11681a98a5b3e0cd181d8eb135d5de4c32f4c6fc7709e022d7585f4"
+ },
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round2-test-types.log",
+ "bytes": 162,
+ "sha256": "de92610b441d6a18f51972d19d9f736c3951f906e91ca4163fd24faa87e52502"
+ }
+ ],
+ "restorer": "f13-f17-examples.mts is unchanged. It restores archive bytes into the explicit directory or ignored output/telescope-family-examples/input; receipts/previews go to explicit out or ignored output/telescope-family-examples. It never reads or generates this retained fixture catalog.",
+ "validation": {
+ "familyExamples": "1 test passed, zero failures/skips; all 18 examples checked",
+ "documentation": "zero errors; 161 links; 63/63 guides reachable",
+ "mutation": "Old F04 acquisition depth fails existing closure check with ENOENT at tests/tools/...; original descriptor bytes restored.",
+ "dispatch": "Existing remaining-family dispatcher test resolves relocated F03 and completes range/chart assertions, then fails at spectrum-preview: The astronomy packages are not installed: node tools/objects/astronomy-packages/toolchain.mts install. Not a passing test; no installation or mocks.",
+ "testTypecheck": {
+ "command": "pnpm typecheck:tests",
+ "exitCode": 0,
+ "typedFiles": 1758,
+ "status": "pass"
+ },
+ "diffCheck": {
+ "command": "git diff --check",
+ "exitCode": 0,
+ "status": "pass"
+ }
+ }
+ },
+ "documentation": {
+ "base": "3aebc31094dff8332755b394b88c708b7c3f1062",
+ "markdownFiles": 10,
+ "localLinksChecked": 161,
+ "errors": [],
+ "documentation": {
+ "guides": 63,
+ "reachableGuides": 63,
+ "illustrations": 51,
+ "usedIllustrations": 51
+ },
+ "scope": "Checks inline Markdown, used reference links and HTML a/img targets and Markdown heading anchors, docs placement and duplicate body accounts. Excludes fenced examples, external URL availability and content quality."
+ },
+ "review": {
+ "receipt": {
+ "packet": "fixture-relocation",
+ "sourceRef": "7a4f38985d98c4c03e7c48710428d9017f45f9d5",
+ "durationMs": 45333,
+ "turns": 1,
+ "successful": true,
+ "completeSentinel": true,
+ "emptyTools": true,
+ "emptyMcp": true,
+ "sourceDrift": [],
+ "dataDrift": [],
+ "preservation": {
+ "sourceRef": "7a4f38985d98c4c03e7c48710428d9017f45f9d5",
+ "files": 44,
+ "exactBytes": 26,
+ "descriptorFiles": 17,
+ "manifestCommandChanges": 17,
+ "allChecksPass": true
+ }
+ },
+ "scope": {
+ "tools/objects/telescopes/family-examples.test.mts": {
+ "sha256": "79f26091a79236938ed6a4a2baf818cf6a48d6c3f07c8b2684180a4dfdf75870",
+ "bytes": 3844,
+ "lines": [
+ [
+ 1,
+ 41
+ ]
+ ]
+ },
+ "tools/objects/telescopes/family-operation.test.mts": {
+ "sha256": "ac29fd2774d5d50150be7166b07ae46e899287843ec3e5307fa10168ce9fc730",
+ "bytes": 18116,
+ "lines": [
+ [
+ 1,
+ 19
+ ],
+ [
+ 59,
+ 78
+ ]
+ ]
+ },
+ "tools/objects/telescopes/families/f13-f17-examples.mts": {
+ "sha256": "ecfb98810a57ea9759e554cbd56337fbbc626b7bac82db7c8fedd50ce0454015",
+ "bytes": 3867,
+ "lines": [
+ [
+ 1,
+ 15
+ ]
+ ]
+ },
+ "tools/objects/telescopes/family-operation.mts": {
+ "sha256": "b4ef99f14b55d6f43dcd032fa45bd5cb3ef0ac29162d00e0370ac10dc8c7d06b",
+ "bytes": 56027,
+ "lines": [
+ [
+ 123,
+ 136
+ ]
+ ]
+ },
+ "tools/audits/check-documentation-links.py": {
+ "sha256": "f6f3e4bf58f91ee6a597f641d2c8ee0d33479ed73656283931c6821be635cc95",
+ "bytes": 7242,
+ "lines": [
+ [
+ 118,
+ 143
+ ]
+ ]
+ },
+ "tests/fixtures/telescope-family-examples/descriptors/F04.json": {
+ "sha256": "2a0b2eba1c17c24f2e6e915b3509c182a0b17286652805b118a0f34dfff584b5",
+ "bytes": 4686,
+ "lines": [
+ [
+ 1,
+ 145
+ ]
+ ]
+ },
+ "tests/fixtures/telescope-family-examples/descriptors/F18.json": {
+ "sha256": "4617e78eaa8e4ec241f6e4be79e0eb20b67747df90444d42d8e685ea778d862a",
+ "bytes": 4231,
+ "lines": [
+ [
+ 1,
+ 167
+ ]
+ ]
+ },
+ "docs/README.md": {
+ "sha256": "792854f9da8c85fdbcc5aaa94cee5fe894dcd79bd88efc81538fd8fc0d68c0c2",
+ "bytes": 8084,
+ "lines": "diff above"
+ },
+ "docs/telescope-family-examples/README.md": {
+ "sha256": "09e01977a1ab9f55ccbdd47c516410a24adb1757def9ac418f3adf6c7924f5ba",
+ "bytes": 3787,
+ "lines": "diff above"
+ }
+ },
+ "preservation": {
+ "summary": {
+ "sourceRef": "7a4f38985d98c4c03e7c48710428d9017f45f9d5",
+ "files": 44,
+ "exactBytes": 26,
+ "descriptorFiles": 17,
+ "manifestCommandChanges": 17,
+ "allChecksPass": true
+ },
+ "pins": [
+ {
+ "old": "docs/telescope-family-examples/artifacts/F01.csv",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F01.csv",
+ "oldBytes": 320309,
+ "newBytes": 320309,
+ "oldSha256": "511587d2174225fee2ea4eaf7050bd580275dc45ef83951144539bc4c61fa64c",
+ "newSha256": "511587d2174225fee2ea4eaf7050bd580275dc45ef83951144539bc4c61fa64c",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F02.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F02.json",
+ "oldBytes": 2462,
+ "newBytes": 2462,
+ "oldSha256": "7d0df1bc4589ac6ca0c7c1389485ab8dcd4e3bb20c5b79b825e93d36f30dce1d",
+ "newSha256": "7d0df1bc4589ac6ca0c7c1389485ab8dcd4e3bb20c5b79b825e93d36f30dce1d",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F03.csv",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F03.csv",
+ "oldBytes": 10007,
+ "newBytes": 10007,
+ "oldSha256": "d6aaea2ee5c61624057ad502bf392bdc6871acf832e5b35a93608464b4988c76",
+ "newSha256": "d6aaea2ee5c61624057ad502bf392bdc6871acf832e5b35a93608464b4988c76",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F04.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F04.json",
+ "oldBytes": 2739,
+ "newBytes": 2739,
+ "oldSha256": "10118793a5f980fe81a9361b100ad3f4fe1a429c52152ed383e36e043f957ae2",
+ "newSha256": "10118793a5f980fe81a9361b100ad3f4fe1a429c52152ed383e36e043f957ae2",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F05.csv",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F05.csv",
+ "oldBytes": 151,
+ "newBytes": 151,
+ "oldSha256": "69f48cfa89a61b1d919592adaec48c0f8f185a46900030462f1198580884152f",
+ "newSha256": "69f48cfa89a61b1d919592adaec48c0f8f185a46900030462f1198580884152f",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F06.csv",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F06.csv",
+ "oldBytes": 343,
+ "newBytes": 343,
+ "oldSha256": "42fecf1d339745021f7aa337f751b5e8339e4ad5ba1abd9808fc17fb7ce9d0c9",
+ "newSha256": "42fecf1d339745021f7aa337f751b5e8339e4ad5ba1abd9808fc17fb7ce9d0c9",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F07.csv",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F07.csv",
+ "oldBytes": 354,
+ "newBytes": 354,
+ "oldSha256": "a6724d8ec1746ff26583e1545b0d2f6b1ec04d5cdcd8250225634db5417aa291",
+ "newSha256": "a6724d8ec1746ff26583e1545b0d2f6b1ec04d5cdcd8250225634db5417aa291",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F08.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F08.json",
+ "oldBytes": 3227,
+ "newBytes": 3227,
+ "oldSha256": "1f4270ec5ad4467645f043486a789bf47e055ab9599a2ad0b984626ed63fdde2",
+ "newSha256": "1f4270ec5ad4467645f043486a789bf47e055ab9599a2ad0b984626ed63fdde2",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F09.csv",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F09.csv",
+ "oldBytes": 30677,
+ "newBytes": 30677,
+ "oldSha256": "b41b12fbd1470980a5f9e2676185d6d7bc52eadb26ba9d7b716fc5e916dfcd72",
+ "newSha256": "b41b12fbd1470980a5f9e2676185d6d7bc52eadb26ba9d7b716fc5e916dfcd72",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F10.csv",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F10.csv",
+ "oldBytes": 1234502,
+ "newBytes": 1234502,
+ "oldSha256": "008a06a057b541851b44aa8c9fc823f01954e395d488bf54d6d04f0c6c47d0dc",
+ "newSha256": "008a06a057b541851b44aa8c9fc823f01954e395d488bf54d6d04f0c6c47d0dc",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F11.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F11.json",
+ "oldBytes": 1816,
+ "newBytes": 1816,
+ "oldSha256": "9324e9afeb0ed225b6279c6b99e135cfad0938caaa22b7be9a926a8d2d8e8536",
+ "newSha256": "9324e9afeb0ed225b6279c6b99e135cfad0938caaa22b7be9a926a8d2d8e8536",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F12.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F12.json",
+ "oldBytes": 153585,
+ "newBytes": 153585,
+ "oldSha256": "fe0e04f589f650ba5ca105f959863493f969e5f0bc07d79b174c97cf47500c22",
+ "newSha256": "fe0e04f589f650ba5ca105f959863493f969e5f0bc07d79b174c97cf47500c22",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F13.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F13.json",
+ "oldBytes": 537,
+ "newBytes": 537,
+ "oldSha256": "b162ce03379dfe3a287994904fc4bac682a8e511e810b9da2eb5d555c3e109d9",
+ "newSha256": "b162ce03379dfe3a287994904fc4bac682a8e511e810b9da2eb5d555c3e109d9",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F14.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F14.json",
+ "oldBytes": 528,
+ "newBytes": 528,
+ "oldSha256": "d13871b14d55804e8cd88da19608ba6a60babc3abffff82b370c9d8dc697a65f",
+ "newSha256": "d13871b14d55804e8cd88da19608ba6a60babc3abffff82b370c9d8dc697a65f",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F15.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F15.json",
+ "oldBytes": 235,
+ "newBytes": 235,
+ "oldSha256": "f9f23442978f2f328d758eda7f24f5501e5d739ff3d0e3222f9e9676213774f3",
+ "newSha256": "f9f23442978f2f328d758eda7f24f5501e5d739ff3d0e3222f9e9676213774f3",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F16.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F16.json",
+ "oldBytes": 399,
+ "newBytes": 399,
+ "oldSha256": "ab61e17f55f19c6cdd90d189f7c85c2b8ec714f7065b448759bffad699ccd534",
+ "newSha256": "ab61e17f55f19c6cdd90d189f7c85c2b8ec714f7065b448759bffad699ccd534",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F17.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F17.json",
+ "oldBytes": 850,
+ "newBytes": 850,
+ "oldSha256": "3ce0da2fb9110c5d78d52bf10f96a534a4b1e1d1cda920e2b0712af6b87c01ef",
+ "newSha256": "3ce0da2fb9110c5d78d52bf10f96a534a4b1e1d1cda920e2b0712af6b87c01ef",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/artifacts/F18.json",
+ "new": "tests/fixtures/telescope-family-examples/artifacts/F18.json",
+ "oldBytes": 1150,
+ "newBytes": 1150,
+ "oldSha256": "850afec6860bc1c07ca50f5da408fcf3f50832f61ee54c8ee351844e5119be13",
+ "newSha256": "850afec6860bc1c07ca50f5da408fcf3f50832f61ee54c8ee351844e5119be13",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F02.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F02.json",
+ "oldBytes": 3289,
+ "newBytes": 3271,
+ "oldSha256": "62fcd738e49d7e17d879a606ec5a4bea036ba5e2917acd6d9a65e5f232f97f60",
+ "newSha256": "420084ef3535cc83d790ef5652ce4b17fcf10dae431de935d6e6a17b29b94866",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F03.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F03.json",
+ "oldBytes": 2924,
+ "newBytes": 2930,
+ "oldSha256": "d7c54fa12a84bb6f3619c9393c7f58a5e41cc8e7ae2e04a18993b88443ac61ed",
+ "newSha256": "b3715ae7dc141dbee9b3a099db71de9a34fdd549c02c53ee399e85eb481adfd2",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F04.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F04.json",
+ "oldBytes": 4677,
+ "newBytes": 4686,
+ "oldSha256": "fa10e0795c6465aa77d802c810a3b07a10e261c247066374b5528ce54ed902ad",
+ "newSha256": "2a0b2eba1c17c24f2e6e915b3509c182a0b17286652805b118a0f34dfff584b5",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F05.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F05.json",
+ "oldBytes": 3645,
+ "newBytes": 3651,
+ "oldSha256": "ab977b771cc7132d6872bbb8a04d125fa71b65555088db01fcfe7ca7a6c715c2",
+ "newSha256": "30fe57d5cac0b04073eff07e9ec798191fd6ef8ff1bf8dee13e38826219ca0b9",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F06.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F06.json",
+ "oldBytes": 3289,
+ "newBytes": 3295,
+ "oldSha256": "ce1ad9cfe37228886bbda9dca735ceb4032f729851b9cbfce95652cd82bc9f2d",
+ "newSha256": "eddf325936f716b7715f46f2c491e724bd2833e1046db2a5c6bc6bd27add8f5f",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F07.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F07.json",
+ "oldBytes": 2778,
+ "newBytes": 2760,
+ "oldSha256": "c1ea1aa867b4dd9634bd6d86bc144ffe14daf3023dbd52f2aa19812a4c57b99e",
+ "newSha256": "8df95c6e1e292a20da0c07481d1e03cdafe442c25c1141f9378e1d263a2b88c3",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F08.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F08.json",
+ "oldBytes": 6636,
+ "newBytes": 6642,
+ "oldSha256": "2a4769accf40d6f4e5163bf8f5abeb1a918caf5cbb9ebbfa4caab9c4cd2158b1",
+ "newSha256": "f22adfc88e14d293d0f7b4f313a3dccbbd288598f1281542add0f34955d69497",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F09.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F09.json",
+ "oldBytes": 4310,
+ "newBytes": 4316,
+ "oldSha256": "6204a72d2c4b93b31a354854af189a8b5ba96cb33ebd415da5e872f6c5e0dae3",
+ "newSha256": "02768efe33be9974fc7fbc8cfdd1bc9c51e3f3e272c86bad62fd2e0920e06767",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F10.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F10.json",
+ "oldBytes": 4715,
+ "newBytes": 4679,
+ "oldSha256": "023170a87411ca8c40b7ca27b5442a22936cc9928cf39d2f0f3d9a7c0489daa4",
+ "newSha256": "0c00ebd8697782fa1b549881c1efadd8ca5dd1bab0278dc2c9293275137cfc24",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F11.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F11.json",
+ "oldBytes": 2705,
+ "newBytes": 2669,
+ "oldSha256": "6e2ac2b409919a0ca9e050d002864dd99f8ff64a44786252d9eb8b5a69752d29",
+ "newSha256": "432abbf3380bc744907b46e3a3c5dcb2aec18a62287a96dffaf9fecaf1410453",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F12.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F12.json",
+ "oldBytes": 3991,
+ "newBytes": 3997,
+ "oldSha256": "2a41d16eb5250a976be591d8c1915d2bf438e8fc1c9188281dc19b861c5b8de8",
+ "newSha256": "7a1e9e8bb63a9e35a8fcbf55532b41347cb77d9143c60b8c91d1c3c7d05e91f8",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F13.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F13.json",
+ "oldBytes": 10231,
+ "newBytes": 10195,
+ "oldSha256": "10cf2dfaef3eb978ca4e3edd4316bae809ea998f98f3557d36849894aba3ba26",
+ "newSha256": "928e541713af255fe8b2b3dc2a7af3030fbcea9768eda8bf81901d4b1666bc93",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F14.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F14.json",
+ "oldBytes": 2363,
+ "newBytes": 2345,
+ "oldSha256": "94aa9668b1f410840391cf726a2894790b5a022041bed91bc7506e78fb296981",
+ "newSha256": "f7156e3b8ca6ace3d02785da72ba9f4a717f3f27f66ec87b19d10fd7976b09b9",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F15.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F15.json",
+ "oldBytes": 3405,
+ "newBytes": 3369,
+ "oldSha256": "5794716a87fe7f9144f579f578c46e24ffc6a32122111f34204004f066057ed9",
+ "newSha256": "7790e595e617e7330f6b204cee88b7a68a3aab403b420ca2749bb9733e761aeb",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F16.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F16.json",
+ "oldBytes": 5092,
+ "newBytes": 5095,
+ "oldSha256": "d7b8449d3f5901de7dc5bbdfb1de54d0b8c44815512aca016cf578acc7cd7037",
+ "newSha256": "45d1ed9e1a1641727d558e58ccaf304d4bb61373d90ef6e134ea3d26d7923584",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F17.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F17.json",
+ "oldBytes": 3618,
+ "newBytes": 3564,
+ "oldSha256": "d51419422be27489a0e4d489aa7051fda0afeec6c9d60b54bc8bc31c38e79961",
+ "newSha256": "d464887ddb725ae357976be42d7d30e05068a352001b5c3326db075d68eba893",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/descriptors/F18.json",
+ "new": "tests/fixtures/telescope-family-examples/descriptors/F18.json",
+ "oldBytes": 4219,
+ "newBytes": 4231,
+ "oldSha256": "b4e12c081d0bd5695d86e4a474eafdda37bf3f04e408c0de00247614a4c42539",
+ "newSha256": "4617e78eaa8e4ec241f6e4be79e0eb20b67747df90444d42d8e685ea778d862a",
+ "kind": "relative path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/manifest.json",
+ "new": "tests/fixtures/telescope-family-examples/manifest.json",
+ "oldBytes": 25150,
+ "newBytes": 25400,
+ "oldSha256": "f6f58874fcc709df139cae1fe72eb1d6945e102cabd76e793e4a564967bc5fc3",
+ "newSha256": "9f06d9f09d17f3db7ba99d29ba2bdb56f27935299c83772862a81a87f7f776f3",
+ "kind": "command path transport only"
+ },
+ {
+ "old": "docs/telescope-family-examples/params/F02.json",
+ "new": "tests/fixtures/telescope-family-examples/params/F02.json",
+ "oldBytes": 106,
+ "newBytes": 106,
+ "oldSha256": "f3d2946202e381939ecd0dddd78fc491cc3feb5e50b2ad269bff5364733fc060",
+ "newSha256": "f3d2946202e381939ecd0dddd78fc491cc3feb5e50b2ad269bff5364733fc060",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/params/F06.json",
+ "new": "tests/fixtures/telescope-family-examples/params/F06.json",
+ "oldBytes": 105,
+ "newBytes": 105,
+ "oldSha256": "2868a5ba16f74eaee6137229c60c557587d2d74b6e5df73837340588160705bb",
+ "newSha256": "2868a5ba16f74eaee6137229c60c557587d2d74b6e5df73837340588160705bb",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/params/F07.json",
+ "new": "tests/fixtures/telescope-family-examples/params/F07.json",
+ "oldBytes": 96,
+ "newBytes": 96,
+ "oldSha256": "0dc96be0ededa57992a6ebf255d70a8add9feaf93cb03899fdb0b389c7cb83d4",
+ "newSha256": "0dc96be0ededa57992a6ebf255d70a8add9feaf93cb03899fdb0b389c7cb83d4",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/params/F10.json",
+ "new": "tests/fixtures/telescope-family-examples/params/F10.json",
+ "oldBytes": 124,
+ "newBytes": 124,
+ "oldSha256": "cf61276bebfd465b6e01d4a8a01757723ec2e81c5c158043c3c2a4bc74959479",
+ "newSha256": "cf61276bebfd465b6e01d4a8a01757723ec2e81c5c158043c3c2a4bc74959479",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/params/F11.json",
+ "new": "tests/fixtures/telescope-family-examples/params/F11.json",
+ "oldBytes": 325,
+ "newBytes": 325,
+ "oldSha256": "ca840fa313c16c9c4606c7efb849108b329dda625605567935384588ff88ae23",
+ "newSha256": "ca840fa313c16c9c4606c7efb849108b329dda625605567935384588ff88ae23",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/params/F13.json",
+ "new": "tests/fixtures/telescope-family-examples/params/F13.json",
+ "oldBytes": 130,
+ "newBytes": 130,
+ "oldSha256": "20cf4eb66ed978177f99a91200d89fe526314d6bd352159fa1e1b442db1f5e0e",
+ "newSha256": "20cf4eb66ed978177f99a91200d89fe526314d6bd352159fa1e1b442db1f5e0e",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/params/F14.json",
+ "new": "tests/fixtures/telescope-family-examples/params/F14.json",
+ "oldBytes": 117,
+ "newBytes": 117,
+ "oldSha256": "9ac35bc28dcd79c762da518096141fc3ba4464ce8a74121de873145ab6a69cdf",
+ "newSha256": "9ac35bc28dcd79c762da518096141fc3ba4464ce8a74121de873145ab6a69cdf",
+ "kind": "exact bytes"
+ },
+ {
+ "old": "docs/telescope-family-examples/params/F15.json",
+ "new": "tests/fixtures/telescope-family-examples/params/F15.json",
+ "oldBytes": 83,
+ "newBytes": 83,
+ "oldSha256": "dea1e9d07e8f940b01a90914bd82a0f0e8decb4b3667eb109af7ffc658ecce7d",
+ "newSha256": "dea1e9d07e8f940b01a90914bd82a0f0e8decb4b3667eb109af7ffc658ecce7d",
+ "kind": "exact bytes"
+ }
+ ],
+ "changes": [
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F02.json",
+ "delta": [
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "new": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F03.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
+ "new": "../../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
+ "new": "../../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F04.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../tools/objects/hst/programs/europa-salt-map.scan.json",
+ "new": "../../../../tools/objects/hst/programs/europa-salt-map.scan.json"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tools/objects/hst/programs/europa-salt-map.scan.json",
+ "new": "../../../../tools/objects/hst/programs/europa-salt-map.scan.json"
+ },
+ {
+ "field": [
+ "members",
+ 1,
+ "path"
+ ],
+ "old": "../../../tools/objects/hst/programs/europa-salt-map.scan.reproduction.json",
+ "new": "../../../../tools/objects/hst/programs/europa-salt-map.scan.reproduction.json"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F05.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../src/objects/haumea/source/photometry/disc-color.json",
+ "new": "../../../../src/objects/haumea/source/photometry/disc-color.json"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../src/objects/haumea/source/photometry/disc-color.json",
+ "new": "../../../../src/objects/haumea/source/photometry/disc-color.json"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F06.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "new": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "new": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F07.json",
+ "delta": [
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/wind-rad1/wi_l2_wav_rad1_20200101_v01.cdf",
+ "new": "../../telescope-families/wind-rad1/wi_l2_wav_rad1_20200101_v01.cdf"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F08.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "new": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "new": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F09.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../src/objects/m1/source/stellar-field.json",
+ "new": "../../../../src/objects/m1/source/stellar-field.json"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../src/objects/m1/source/stellar-field.json",
+ "new": "../../../../src/objects/m1/source/stellar-field.json"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F10.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
+ "new": "../../telescope-families/chandra-events/acisf06431N003_evt2.fits.gz"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
+ "new": "../../telescope-families/chandra-events/acisf06431N003_evt2.fits.gz"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F11.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
+ "new": "../../telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
+ "new": "../../telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F12.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
+ "new": "../../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
+ "new": "../../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F13.json",
+ "delta": [
+ {
+ "field": [
+ "dataset",
+ "acquisition",
+ "identity"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "new": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits"
+ },
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "new": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F14.json",
+ "delta": [
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f14-healpix/bayestar.fits.gz",
+ "new": "../../telescope-families/f14-healpix/bayestar.fits.gz"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F15.json",
+ "delta": [
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/geographos-radar/ge007.fit",
+ "new": "../../telescope-families/geographos-radar/ge007.fit"
+ },
+ {
+ "field": [
+ "members",
+ 1,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/geographos-radar/ge007.lbl",
+ "new": "../../telescope-families/geographos-radar/ge007.lbl"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F16.json",
+ "delta": [
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../src/objects/stellar-neighbourhood/object.json",
+ "new": "../../../../src/objects/stellar-neighbourhood/object.json"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F17.json",
+ "delta": [
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p.fit",
+ "new": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p.fit"
+ },
+ {
+ "field": [
+ "members",
+ 1,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.fit",
+ "new": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.fit"
+ },
+ {
+ "field": [
+ "members",
+ 2,
+ "path"
+ ],
+ "old": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.xml",
+ "new": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.xml"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/descriptors/F18.json",
+ "delta": [
+ {
+ "field": [
+ "members",
+ 0,
+ "path"
+ ],
+ "old": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
+ "new": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json"
+ },
+ {
+ "field": [
+ "members",
+ 1,
+ "path"
+ ],
+ "old": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "new": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json"
+ },
+ {
+ "field": [
+ "components",
+ 0,
+ "name"
+ ],
+ "old": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
+ "new": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json"
+ },
+ {
+ "field": [
+ "components",
+ 1,
+ "name"
+ ],
+ "old": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "new": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json"
+ }
+ ]
+ },
+ {
+ "file": "tests/fixtures/telescope-family-examples/manifest.json",
+ "delta": [
+ {
+ "field": [
+ "examples",
+ 1,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F02.json nd-spectrum --params docs/telescope-family-examples/params/F02.json --out example-output/F02",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F02.json nd-spectrum --params tests/fixtures/telescope-family-examples/params/F02.json --out example-output/F02"
+ },
+ {
+ "field": [
+ "examples",
+ 2,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F03.json spectrum-export --out example-output/F03",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F03.json spectrum-export --out example-output/F03"
+ },
+ {
+ "field": [
+ "examples",
+ 3,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F04.json slit-scan-inspect --out example-output/F04",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F04.json slit-scan-inspect --out example-output/F04"
+ },
+ {
+ "field": [
+ "examples",
+ 4,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F05.json photometry-export --out example-output/F05",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F05.json photometry-export --out example-output/F05"
+ },
+ {
+ "field": [
+ "examples",
+ 5,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F06.json time-series-select --params docs/telescope-family-examples/params/F06.json --out example-output/F06",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F06.json time-series-select --params tests/fixtures/telescope-family-examples/params/F06.json --out example-output/F06"
+ },
+ {
+ "field": [
+ "examples",
+ 6,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F07.json dynamic-spectrum-export --params docs/telescope-family-examples/params/F07.json --out example-output/F07",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F07.json dynamic-spectrum-export --params tests/fixtures/telescope-family-examples/params/F07.json --out example-output/F07"
+ },
+ {
+ "field": [
+ "examples",
+ 7,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F08.json table-inspect --out example-output/F08",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F08.json table-inspect --out example-output/F08"
+ },
+ {
+ "field": [
+ "examples",
+ 8,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F09.json astrometry-export --out example-output/F09",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F09.json astrometry-export --out example-output/F09"
+ },
+ {
+ "field": [
+ "examples",
+ 9,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F10.json selected-events --params docs/telescope-family-examples/params/F10.json --out example-output/F10",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F10.json selected-events --params tests/fixtures/telescope-family-examples/params/F10.json --out example-output/F10"
+ },
+ {
+ "field": [
+ "examples",
+ 10,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F11.json uvfits-visibility-export --params docs/telescope-family-examples/params/F11.json --out example-output/F11",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F11.json uvfits-visibility-export --params tests/fixtures/telescope-family-examples/params/F11.json --out example-output/F11"
+ },
+ {
+ "field": [
+ "examples",
+ 11,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F12.json v2-diagnostics --out example-output/F12",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F12.json v2-diagnostics --out example-output/F12"
+ },
+ {
+ "field": [
+ "examples",
+ 12,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F13.json stokes-spectrum --params docs/telescope-family-examples/params/F13.json --out example-output/F13",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F13.json stokes-spectrum --params tests/fixtures/telescope-family-examples/params/F13.json --out example-output/F13"
+ },
+ {
+ "field": [
+ "examples",
+ 13,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F14.json healpix-select --params docs/telescope-family-examples/params/F14.json --out example-output/F14",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F14.json healpix-select --params tests/fixtures/telescope-family-examples/params/F14.json --out example-output/F14"
+ },
+ {
+ "field": [
+ "examples",
+ 14,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F15.json radar-profile --params docs/telescope-family-examples/params/F15.json --out example-output/F15",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F15.json radar-profile --params tests/fixtures/telescope-family-examples/params/F15.json --out example-output/F15"
+ },
+ {
+ "field": [
+ "examples",
+ 15,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F16.json spatial-inspect --out example-output/F16",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F16.json spatial-inspect --out example-output/F16"
+ },
+ {
+ "field": [
+ "examples",
+ 16,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F17.json near-msi-inspect --out example-output/F17",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F17.json near-msi-inspect --out example-output/F17"
+ },
+ {
+ "field": [
+ "examples",
+ 17,
+ "command"
+ ],
+ "old": "telescope family-run docs/telescope-family-examples/descriptors/F18.json enumerate-members --out example-output/F18",
+ "new": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F18.json enumerate-members --out example-output/F18"
+ }
+ ]
+ }
+ ]
+ },
+ "report": "**Verdict:** Accept. The 44 data files moved with path transport only; payloads were not scientifically regenerated. No P0, P1, or borderline P2 on this relocation.\n\n## Findings\n\nNone blocking.\n\n**P2 (ordinary, non-blocking):** F18 `dataset.acquisition.identity` stays a repo-root string (`tools/objects/pds/...`) while member paths gained one `../`. Loader opens members from the descriptor directory (`family-operation.mts` 130–133), not identity. The new existence check only runs when identity starts with `../` (`family-examples.test.mts` 33). Pre-existing split, not a broken target after the move.\n\n## Independent check inference\n\nThe script is a preservation check, not scientific acceptance.\n\n- Same 44 non-Markdown names, `docs/…` → `tests/fixtures/…`.\n- `artifacts/` and `params/`: exact bytes (26).\n- 17 descriptors (F02–F18): JSON keys/values equal except `../` strings whose resolved targets match old vs new parents.\n- `manifest.json`: 17 `command` strings, prefix replace only (F02–F18). F01 stays `telescope export`.\n- `allChecksPass` follows those asserts; it does not re-qualify science.\n\nPath hop is correct: old descriptors sit three levels under root, new ones four. Repo-root refs `../../../` → `../../../../`. Same-fixtures refs `../../../tests/fixtures/telescope-families/…` → `../../telescope-families/…`.\n\n## Transport, owners, checker\n\n- Loader still `resolve(dirname(descriptorPath), member.path)` (`family-operation.mts` 130–133).\n- Tests point at `tests/fixtures/telescope-family-examples` (`family-examples.test.mts` 9; `family-operation.test.mts` 61).\n- Guide stays Markdown and is indexed (`docs/README.md`; `docs/telescope-family-examples/README.md`).\n- Docs checker unchanged: non-guide files under `docs/` still fail (`check-documentation-links.py` 118–124). Failures were not ignored.\n- F13/F17 restorer still writes `output/telescope-family-examples` (`f13-f17-examples.mts` 15). No docs path.\n\n## Scope / limits\n\nCovered: path transport, byte/JSON preservation vs `7a4f389`, loader/command/test refs, restore/prepare locations, docs contract vs remaining guide.\n\nNot covered: native scientific exports, decoder/Python env, unrelated telescope architecture. Those were already unavailable; this move does not claim they pass.\n\n=== REPORT COMPLETE ===\n"
+ },
+ "productionBeforeRepair": {
+ "head": "7a4f38985d98c4c03e7c48710428d9017f45f9d5",
+ "url": "https://github.com/layoutit/css.earth/actions/runs/35541285697/job/106159310455",
+ "result": "passed",
+ "coverage": "Production build/R2 metadata identity and Venus/Mercury navigation,81 content-addressed requests and zero same-origin scene requests. This does not inspect Omega morphology.",
+ "reuse": "Fixture relocation changes no application,renderer,prepared-asset or navigation-test owner; successful production evidence is retained for that unchanged scope."
+ },
+ "unvalidated": "The existing native dispatcher test reaches relocated source operations then cannot run the plotting boundary without the pinned Python environment. No dependency installation or skip added."
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/independent-review.json b/src/objects/omega-centauri/evidence/2026-09-21/independent-review.json
new file mode 100644
index 0000000000..25fc97fa40
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/independent-review.json
@@ -0,0 +1,369 @@
+{
+ "scope": "Main merge and inherited typing repair; provenance/catalogue reviews used the staged pre-authoring binding. Final regenerated asset identities are independently verified in dependent-r2-restore.json.",
+ "verifiedReceipts": [
+ {
+ "packet": "provenance-merge",
+ "durationMs": 69034,
+ "turns": 1,
+ "completeSentinel": true,
+ "successful": true,
+ "emptyTools": true,
+ "emptyMcp": true,
+ "stagedSourceChanges": []
+ },
+ {
+ "packet": "catalogue-focus-merge",
+ "durationMs": 46318,
+ "turns": 1,
+ "completeSentinel": true,
+ "successful": true,
+ "emptyTools": true,
+ "emptyMcp": true,
+ "stagedSourceChanges": []
+ },
+ {
+ "packet": "types-cleanup",
+ "durationMs": 24374,
+ "turns": 1,
+ "completeSentinel": true,
+ "successful": true,
+ "emptyTools": true,
+ "emptyMcp": true,
+ "worktreeSourceChanges": []
+ }
+ ],
+ "reports": [
+ {
+ "name": "provenance-merge",
+ "report": "**Yes.** Object-scoped authoring and prepared-reader deploy compose: `--object` only narrows `prepareVolumeProvenance`; default remains all volumes; `--catalog-only` reads installed prepared identities and writes catalogues only; Omega stays in the volume set with the same limitation fields; Nearby-Universe stays a context/field bind, not a volume authoring target.\n\n## P0 / P1 / borderline P2\n\nNone observed in the staged merge interaction.\n\n## Composition (observed)\n\n**Authoring scope does not change the default.** `objectId` is optional. Unset, the folder filter is still every directory (`tools/prepare-volume-provenance.mts:247`). Set, `sourceId` runs first (`:242`) and the loop keeps only that folder; missing/non-volume ids fail after the loop (`:362`). CLI allows zero args or one `--object=` and only then passes `objectId` (`:375-381`). `writeVolumeProvenance` still writes whatever `prepareVolumeProvenance` returned (`:366-372`), so scoped CLI writes that object only; unscoped CLI still prepares every volume.\n\n**Deploy does not generate or overwrite object packages.** `prepareFacilities` on `publish === 'catalogues'` uses `readPreparedVolumeProvenance` + `readPreparedContextProvenance` (`tools/prepare-facilities.mts:145-148`). The reader always sets `outputs: []` (`tools/prepare-volume-provenance.mts:173`). Writes are `writePreparedSet(catalogueOutputs)` only (`tools/prepare-facilities.mts:177`). CLI `--catalog-only` is the only extra flag (`:182-186`). `prepare:deploy` now ends at `prepare:deploy-catalogues` (`node tools/prepare-facilities.mts --catalog-only`), not galaxy-field generation.\n\n**Reader vs author stay on separate Options.** `readPreparedVolumeProvenance` has no `objectId` (`:143-145`) and always walks all volume presentations. Facilities never pass `objectId` into `prepareVolumeProvenance` (`:148`). Scoped authoring cannot leak into catalog-only deploy.\n\n**Omega remains a volume candidate with original limitation wiring.** All-object authoring still expects `omega-centauri` with 2 lenses (`tools/prepare-volume-provenance.test.mts:31`). Scoped authoring is tested on that id (`:16-24`). Product `limitations` are still `[lens.description, lens.detail]` (`tools/prepare-volume-provenance.mts:319`). Default route remains `/sun/?focus=…` when unhosted (`:359`, test `:23`).\n\n**Nearby-Universe is not a volume and is not `--object`-prepared.** Volume authoring continues only on `cssearth-volume-presentation-source@1` (`:250`). Nearby `object.json` is `galaxy-point-field` with prepared `points.json` (`src/objects/nearby-universe/object.json:4-14`). Setup metadata treats it as context (`provenance` + `runtime-assets.json`), not a volume schema (`tools/setup-volume-metadata.mts:25-27`). The metadata test includes `nearby-universe` and restores only `presentation.json` / `provenance.json` (`tools/setup-volume-metadata.test.mts:55-58,70`). Deploy volume-reader ignores source-only context presentations (`tools/prepare-volume-provenance.mts:156-158`; `tools/prepare-context-availability.test.mts:85-90`).\n\n**Nearby pins are internally consistent in the staged bind.** `object.json` prepared sha256 `c70262ba…` matches `prepared/manifest.json` `points.json` and `runtime-assets.json` `points.json`. `cloud.webp` sha256 `ddfc2751…` matches manifest and runtime-assets. Manifest still pins `source/presentation.json` and `src/objects/sun/source/navigation/universe.json` (`src/objects/nearby-universe/source/manifest.json:104-130`). `prepare-points.mts` still reads that manifest, presentation, and universe frame (`tools/galaxy-field/prepare-points.mts:13-22`). That generator is not on `prepare:deploy`.\n\n**No new publication in this interaction.** Catalog-only writes site catalogues only. Volume reader contributes no package outputs.\n\n## Ordinary P2\n\n**Authoring self-fingerprint will move; deploy receipts will not.** Adding `objectId` changes `prepare-volume-provenance.mts`, which is hashed as the generator (`:244, :325`). Later `prepareVolumeProvenance` / unscoped CLI output is a new recovered document. Catalog-only deploy still binds whatever `prepared/provenance.json` setup restored. Old R2 authoring receipts do not prove a post-merge authoring run.\n\n## Uncertainty (not treated as defects)\n\n`readPreparedContextProvenance` body is not in this packet. Nearby catalogue inclusion on deploy is inferred from `prepareFacilities` concatenating that reader (`tools/prepare-facilities.mts:146-147`) and from metadata restore (`tools/setup-volume-metadata.mts:26-29`). Empty `outputs` is observed only on the volume reader (`:173`).\n\n=== REPORT COMPLETE ===\n",
+ "scope": [
+ {
+ "path": "tools/prepare-volume-provenance.mts",
+ "sha256": "d923c49fa343ce93d64590dd10b4e40b1f843e306d5e7014144a587815f4518d",
+ "bytes": 31380,
+ "ranges": [
+ [
+ 116,
+ 188
+ ],
+ [
+ 238,
+ 259
+ ],
+ [
+ 319,
+ 385
+ ]
+ ]
+ },
+ {
+ "path": "tools/prepare-facilities.mts",
+ "sha256": "48f02c955fc982e8b3175a95c561016c7bb87742d259a406647f84cb19028697",
+ "bytes": 16849,
+ "ranges": [
+ [
+ 132,
+ 191
+ ]
+ ]
+ },
+ {
+ "path": "tools/setup-volume-metadata.mts",
+ "sha256": "da5b0d15e8b966794c344ab9e746c6d4657633b76da62f9afd242f26a13358f9",
+ "bytes": 3276,
+ "ranges": null
+ },
+ {
+ "path": "site/volume-presentation.mts",
+ "sha256": "4bfb6270a8ec2e62152060f73472d35905950cb7bb4b9aed69d61e2c5f43e117",
+ "bytes": 1936,
+ "ranges": null
+ },
+ {
+ "path": "tools/prepare-context-availability.test.mts",
+ "sha256": "3acb3627f83563abc35ca43bfb37227f4594b6ce4b242dac6eeab69f783a58b8",
+ "bytes": 6431,
+ "ranges": [
+ [
+ 73,
+ 100
+ ]
+ ]
+ },
+ {
+ "path": "tools/prepare-volume-provenance.test.mts",
+ "sha256": "0050fec1efc5e7d2ac17d39794023bc8655ecb2648e7e6ea9e213f4de9c31f7c",
+ "bytes": 26438,
+ "ranges": [
+ [
+ 1,
+ 44
+ ]
+ ]
+ },
+ {
+ "path": "tools/setup-volume-metadata.test.mts",
+ "sha256": "634fa68b9298bad26fcbbd7ab1aaf81d66863ed63cb47491580f0fbf748749ba",
+ "bytes": 4880,
+ "ranges": null
+ },
+ {
+ "path": "tools/galaxy-field/prepare-points.mts",
+ "sha256": "419ad3ffff3886232cb2d13a5c36ad58759611a98ca6ab64ed04d8b073683688",
+ "bytes": 5856,
+ "ranges": [
+ [
+ 1,
+ 27
+ ]
+ ]
+ },
+ {
+ "path": "src/objects/nearby-universe/source/manifest.json",
+ "sha256": "5e3e105e4b1b4f70b4abae418d077c274f23686dec5fbc37e7b8045bed3df924",
+ "bytes": 6599,
+ "ranges": [
+ [
+ 100,
+ 140
+ ]
+ ]
+ },
+ {
+ "path": "src/objects/nearby-universe/object.json",
+ "sha256": "ab70c155a8adf2a8286aefd161b1dd5871859e58abdd01a769791fa4e1584ad4",
+ "bytes": 456,
+ "ranges": null
+ },
+ {
+ "path": "src/objects/nearby-universe/prepared/manifest.json",
+ "sha256": "866e8cd54f11bf54c7842ef63bf187e45d8a29287c48144d497bae1735aadda8",
+ "bytes": 355,
+ "ranges": null
+ },
+ {
+ "path": "src/objects/nearby-universe/runtime-assets.json",
+ "sha256": "7ef6d9e20e1e80ce1f73473243b5d035c757bec2bee5d656f0690084fa6ccf7e",
+ "bytes": 713,
+ "ranges": null
+ }
+ ]
+ },
+ {
+ "name": "catalogue-focus-merge",
+ "report": "No P0, P1, or borderline P2 merge interaction. Native scene routes, prepared-focus identity, selection owner, and background asset mapping still compose.\n\n**P0 / P1 / borderline P2**\n\nNone observed.\n\nNative scene vs prepared-focus identity is split on the row, not collapsed into one owner key: scene rows keep `objectId` and drop `preparedFocusId`; prepared-focus rows set `preparedFocusId` from the catalogue `id` (`object.focusId`) and drop `objectId` (`site/catalogue-window.mts:87-93`, `site/prepared-catalogue-index.mts:22-23,46`). Fragment prefetch still keys native entry by pathname and ignores prepared-focus anchors (`site/navigation-fragments.mts:146-147`).\n\nSelection still lands on the same navigation owner: window selection is `selectedObjectName` plus `preparedFocus?.id` (`site/planet-shell-client.mts:1077-1079`); restore still `owner.setPreparedFocus` then `layer.selectGalaxy` (`site/prepared-context-navigation.mts:169-171`). Close-up suppression is unchanged in runtime: detailed focus is only installed when the selected record’s `detailedObjectId` is a declared image-layer or volume-lens bank (`src/renderers/css/universe/prepared-universe-runtime.ts:412-423`).\n\nFocus-only restore still waits on `prepareFocus` then flies; `v` still skips flight (`site/prepared-context-navigation.mts:185-179`). Catalogue records still carry classification, aliases (`searchNames`), source document, and `preparedFocusId` (`site/prepared-catalogue-index.mts:26-27,48-50,36,56`, `site/planet-shell-client.mts:690-707`, `site/catalogue-window.mts:77-78,84-92`).\n\nBackground points now resolve through the same asset-origin `resourceSet` as other context objects (`site/application-world-context.mts:68,113`). That does not touch focus identity or the detailed-focus gate.\n\n**Ordinary P2**\n\n`markSelection` both calls `catalogueWindow.setSelection` and re-walks every `.planet-object-link`, including virtual rows (`site/planet-shell-client.mts:1079-1086`). Observed: redundant class/`aria-current` writes. Not a second owner.\n\n**Uncertainty (not a finding)**\n\n`object.route` / `object.kind` on `PLANET_SEARCH_OBJECTS` are not in this packet; identity here is only as materialized in the index and row binder. Prepared-context navigation and detailed-focus runtime were given as byte-unchanged vs `bb7cab3`.\n\n=== REPORT COMPLETE ===\n",
+ "scope": [
+ {
+ "path": "site/prepared-catalogue-index.mts",
+ "sha256": "a179e9299d1a8c3c9cca6ebfb0de908a01ae3ea733e9a1bc5eb84e7dd61df08b",
+ "bytes": 3903,
+ "ranges": null
+ },
+ {
+ "path": "site/catalogue-index.mts",
+ "sha256": "e6f0d6e96312e88ead636ce3ca36fb19eb7a83a12d220f18bd2936336d9a6ed5",
+ "bytes": 4096,
+ "ranges": null
+ },
+ {
+ "path": "site/catalogue-window.mts",
+ "sha256": "8cace8dfc4766a634a7130904ff55a36d07177f4fbc63e15c2f0f2bf7988e380",
+ "bytes": 9261,
+ "ranges": [
+ [
+ 52,
+ 116
+ ],
+ [
+ 178,
+ 215
+ ]
+ ]
+ },
+ {
+ "path": "site/planet-shell-client.mts",
+ "sha256": "3ad1ee46c0b086b4e901c16c6e45bd619372d2c2b73eada60c0d2498629f74b2",
+ "bytes": 81490,
+ "ranges": [
+ [
+ 679,
+ 738
+ ],
+ [
+ 747,
+ 785
+ ],
+ [
+ 1028,
+ 1100
+ ]
+ ]
+ },
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "sha256": "67286a0589f42d990d467d5c1e199661bc8b98b7102d152c527f34fc095e4a36",
+ "bytes": 13264,
+ "ranges": [
+ [
+ 96,
+ 131
+ ],
+ [
+ 144,
+ 188
+ ]
+ ]
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-universe-runtime.ts",
+ "sha256": "fd0d735b8f44cd639e3c9cf5333a4f4fbef1a2c9dbbbf8d288cf59a4441dae2f",
+ "bytes": 48720,
+ "ranges": [
+ [
+ 409,
+ 438
+ ]
+ ]
+ },
+ {
+ "path": "site/navigation-fragments.mts",
+ "sha256": "11ff995039d6cef0c2fed9f4aa29d8faa4bb928ec6b5de32b518caf33b9a3343",
+ "bytes": 8492,
+ "ranges": [
+ [
+ 132,
+ 160
+ ]
+ ]
+ },
+ {
+ "path": "site/application-world-context.mts",
+ "sha256": "a641d9783889974b90c46e8e64a0745f72e26a8aa1d1b1e14dbdd7765b2e666e",
+ "bytes": 24520,
+ "ranges": [
+ [
+ 43,
+ 122
+ ]
+ ]
+ },
+ {
+ "path": "site/test/prepared-context-navigation.test.mts",
+ "sha256": "bc80889dedb3c2b7906dd4792c7be68119442856bae95e4562bbf83c2c2713ba",
+ "bytes": 23828,
+ "ranges": [
+ [
+ 289,
+ 365
+ ]
+ ]
+ },
+ {
+ "path": "site/test/catalogue-window.test.mts",
+ "sha256": "ce4bf379d55e307e7ce1b95620cb716ff6c776e4342cdafb31f710e3def77d2f",
+ "bytes": 3301,
+ "ranges": null
+ }
+ ]
+ },
+ {
+ "name": "types-cleanup",
+ "report": "**Accept.** No introduced P0, P1, or borderline P2 in these seven paths.\n\nThe edits are TypeScript 6 narrowing and union cleanup. Runtime producer behavior, F16 operation order/values, and VO plan inputs stay the same. Test changes tighten discriminants; they do not skip assertions or invent fixture facts.\n\n### Findings\n\nNone at P0 / P1 / borderline P2.\n\n### Change class\n\n| Path | Class | Check |\n|---|---|---|\n| `family-request.mts` | erased type | `Partial>` matches `assessFamilyRequest`, which only writes present keys (`out` at L46–56). Absent criteria stay omitted, not `undefined` slots. |\n| `family-operation.mts` | erased type | `dynamic-spectrum-inspect` is a separate no-payload arm (L50) vs select/export/spectrum/time-series (L56–58). `ALLOWED` / `NEEDS_PARAMETERS` / `customOutput` (L84–85, L161) still treat inspect as parameter-free. |\n| `f16-spatial-physical.mts` | runtime-equivalent expression | Same three defs, same order, same `fixed` (`{}` / `{kind}`), same `map` (L4). |\n| `f14-healpix.test.mts` | test | Extra `kind === 'native'` then `convention` (L3). Stronger, not weaker. |\n| `local-import.test.mts` | test | `'familyOperations' in inspected && …some(healpix-select && available)` (L60). Missing key fails; empty list fails. |\n| `session.test.mts` | test | `'satisfaction' in result` then `status === 'unresolved'` (L120–121). |\n| `vo/session.test.mts` | test | Clone is `{ ...request, wavelengthMicrometres: request.wavelengthMicrometres }` after asserting the field (L105–107). Actual wavelengths and remaining plan fields are kept. |\n\n### Uncertainties / verification\n\nTool and test-graph TypeScript passed (1758 files). Narrow: 23/29 pass; the other 6 need the pinned astronomy Python env — not treated as a code defect, and not claimed as native-boundary success. Parent app merge builds do not prove telescope native deps.\n\n=== REPORT COMPLETE ===\n",
+ "scope": {
+ "tools/objects/telescopes/families/f16-spatial-physical.mts": {
+ "sha256": "635e1b6e6bea9e3753267aebee52c1a30e5d66f08a12caf2fd7600a727508fb0",
+ "bytes": 5507,
+ "lines": [
+ [
+ 1,
+ 5
+ ]
+ ]
+ },
+ "tools/objects/telescopes/family-operation.mts": {
+ "sha256": "b4ef99f14b55d6f43dcd032fa45bd5cb3ef0ac29162d00e0370ac10dc8c7d06b",
+ "bytes": 56027,
+ "lines": [
+ [
+ 33,
+ 86
+ ],
+ [
+ 145,
+ 150
+ ],
+ [
+ 161,
+ 161
+ ],
+ [
+ 255,
+ 268
+ ]
+ ]
+ },
+ "tools/objects/telescopes/family-request.mts": {
+ "sha256": "7a030a9ea8013200647bffba2410947a02d22c9653abb1acb9837add41f7a9dd",
+ "bytes": 8835,
+ "lines": [
+ [
+ 1,
+ 57
+ ]
+ ]
+ },
+ "tools/objects/telescopes/families/f14-healpix.test.mts": {
+ "sha256": "bb9bd4a6a1b9f939e1190c9eee7d0cf20a59a54c186a3c4362c869790ec4c521",
+ "bytes": 3768,
+ "lines": [
+ [
+ 1,
+ 5
+ ]
+ ]
+ },
+ "tools/objects/telescopes/local-import.test.mts": {
+ "sha256": "e7991ab4b9984a618ed782be45f2fb93512f53c4ef5d580041e0b80e956009c0",
+ "bytes": 6659,
+ "lines": [
+ [
+ 50,
+ 65
+ ]
+ ]
+ },
+ "tools/objects/telescopes/session.test.mts": {
+ "sha256": "482ea175375730473543d4dbb35fda7711a5d4d90bf3608dbb806578e16f365a",
+ "bytes": 12789,
+ "lines": [
+ [
+ 109,
+ 134
+ ]
+ ]
+ },
+ "tools/objects/telescopes/vo/session.test.mts": {
+ "sha256": "f11ab27f7c321e6db1acc5f8e7c4f03569b219184ec1c9bf99348744944fabd9",
+ "bytes": 11819,
+ "lines": [
+ [
+ 70,
+ 134
+ ]
+ ]
+ },
+ "tools/objects/telescopes/family-handlers.mts": {
+ "sha256": "a2be2f33c776aedc45372e13a86583dfdb81787834a98a2f5b3b17e7377fb4ba",
+ "bytes": 15080,
+ "lines": [
+ [
+ 23,
+ 38
+ ]
+ ]
+ }
+ }
+ }
+ ],
+ "disposition": "No introduced P0/P1/borderline P2 or actionable cleanup finding. Compiler identity consequence is resolved by final asset publication; incoming redundant selection writes are not a new integration defect. Context reader returns no outputs. No scientific acceptance inferred."
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/merge-validation.json b/src/objects/omega-centauri/evidence/2026-09-21/merge-validation.json
new file mode 100644
index 0000000000..acec11b5eb
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/merge-validation.json
@@ -0,0 +1,263 @@
+{
+ "schema": "cssearth-omega-merge-evidence@1",
+ "parents": [
+ "bb7cab3c2e5be0b58a63e2cbeca33ca94a7b3b5a",
+ "3aebc31094dff8332755b394b88c708b7c3f1062"
+ ],
+ "testedState": "Uncommitted merge plus regenerated dependent asset inventories. Incoming main contains catalogue transport changes; shared native-focus and detailed-context owners are unchanged. Typing issues in seven inherited telescope source/test files are repaired without changing the application renderer. The exact repair evidence is linked separately.",
+ "recordedAt": "2026-09-20T22:10:59.269Z",
+ "checks": [
+ {
+ "command": "pnpm build",
+ "result": "passed",
+ "pages": 995
+ },
+ {
+ "command": "pnpm test:renderer",
+ "result": "passed",
+ "files": 84,
+ "tests": 594
+ },
+ {
+ "command": "node --experimental-test-module-mocks --import ./site/test/navigation-router-context.mts --test site/test/prepared-context-navigation.test.mts site/test/navigation-lifecycle.test.mts site/test/scene-session.test.mts site/test/navigation-router.test.mts site/test/router-runtime.test.mts site/test/catalogue-window.test.mts site/test/catalogue-fragment-loader.test.mts site/test/catalogue-fragment-pin.test.mts",
+ "result": "passed",
+ "tests": 104
+ },
+ {
+ "command": "node --test tools/prepare-volume-provenance.test.mts tools/setup-volume-metadata.test.mts tools/prepare-context-availability.test.mts tools/context-provenance.test.mts tools/nebula-provenance-contract.test.mts tools/object-package-consistency.test.mts",
+ "result": "passed",
+ "tests": 41
+ },
+ {
+ "command": "pnpm typecheck:shell; pnpm typecheck:renderer; pnpm check:typescript-ownership",
+ "result": "passed"
+ },
+ {
+ "command": "node tests/objects/browser/omega-centauri/native-focus-browser.mts http://127.0.0.1:4219 output/pr390-audit/ci-head-bb7cab3/native",
+ "result": "passed",
+ "receipt": "native-focus.json"
+ },
+ {
+ "command": "pnpm typecheck:tools",
+ "result": "passed"
+ },
+ {
+ "command": "pnpm typecheck:tests",
+ "result": "passed",
+ "files": 1758
+ },
+ {
+ "command": "Standard runtime publisher and independent cold/warm restoration",
+ "result": "passed",
+ "receipt": "dependent-r2-restore.json",
+ "files": 200,
+ "uploadedNewProvenanceKeys": 3
+ },
+ {
+ "result": "partial",
+ "claim": "Targeted inherited telescope typing repair behaviors",
+ "tests": 29,
+ "passed": 23,
+ "failed": 6,
+ "receipt": "telescope-types.json",
+ "environmentReceipt": "telescope-environment.json",
+ "limitation": "All six require the absent pinned astronomy decoder environment; four direct failures and two retained local-import validator refusals."
+ },
+ {
+ "result": "passed",
+ "claim": "Independent final merge/typing review",
+ "receipt": "independent-review.json"
+ }
+ ],
+ "reusedEvidence": {
+ "baseRevision": "bb7cab3c2e5be0b58a63e2cbeca33ca94a7b3b5a",
+ "pins": [
+ {
+ "path": "site/prepared-context-navigation.mts",
+ "bytes": 13264,
+ "sha256": "67286a0589f42d990d467d5c1e199661bc8b98b7102d152c527f34fc095e4a36"
+ },
+ {
+ "path": "src/objects/omega-centauri/investigations.json",
+ "bytes": 11844,
+ "sha256": "6733ae6b94b38f0eede05c2d4d5adf0b060cb3e25ee9266a70f7a07ba98103e7"
+ },
+ {
+ "path": "src/objects/omega-centauri/object.json",
+ "bytes": 1116,
+ "sha256": "7846b5714f0d3c55f547ab5207914ee7ec1494c82413b650a20a3f4b91242781"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared-assets.json",
+ "bytes": 10599,
+ "sha256": "a307635f2e3b99073e303b62fc7ec43ca731ea9c99a4d47df411c49e921c4fbd"
+ },
+ {
+ "path": "src/objects/omega-centauri/runtime-assets.json",
+ "bytes": 595,
+ "sha256": "e30940cfcec240029ada2ddd666379804e57d863087d97a7ad91be598cb0f5bf"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs.json.gz",
+ "bytes": 2277027,
+ "sha256": "869e9ab24b644c4ea7d04cf2d4e2c719445c205b879eaebf13a420c7d7d75eac"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/01-compiler.json",
+ "bytes": 1361,
+ "sha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json",
+ "bytes": 2508,
+ "sha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/03-observation-structures.json",
+ "bytes": 397,
+ "sha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json",
+ "bytes": 36711,
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json",
+ "bytes": 3842,
+ "sha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/delivery.json",
+ "bytes": 2436,
+ "sha256": "013ec10f387fb5b8ba47ba08412151abce621df4b48b748a68a795c579cbe2aa"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/manifest.json",
+ "bytes": 11285,
+ "sha256": "8048b39285f795d9a06bf824f00b241b7800edfc4f1e13253f750fca4571b569"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/presentation.json",
+ "bytes": 6278,
+ "sha256": "a3e6293031bf6c92c90fd14cc3c9f2329f8bbe7389c443ee6fe8c832d7740859"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/provenance-references.json",
+ "bytes": 1856,
+ "sha256": "9a749e5f63493aa373c47d09b1bfec5bd312c32b6896aafd0d51cb7a29e1d5d4"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/replay-references.json",
+ "bytes": 1302,
+ "sha256": "d289333b8061b55f07f97bad5bef7a515123cc2e5bf845dec0a50724ba6df358"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/request.json",
+ "bytes": 365,
+ "sha256": "043c6218aedd90269ec68822cfa2d729d731b7995150c0e01af8b5c68127e241"
+ },
+ {
+ "path": "src/renderers/css/sky/prepared-sky-runtime.test.ts",
+ "bytes": 52229,
+ "sha256": "3652d5086c4422b64ee66347d6909617693f443e1c7e577452796d5d4a5805e1"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.test.ts",
+ "bytes": 1302,
+ "sha256": "11a9abf0d929d27d84c698e3a1e9684778d65eafe7eb29b39a747e834b999beb"
+ },
+ {
+ "path": "src/renderers/css/universe/detailed-focus-context.ts",
+ "bytes": 873,
+ "sha256": "12a727775a8277fede7cd743642b960a30c10a7334f2e47186d97b6e8d5413f4"
+ },
+ {
+ "path": "src/renderers/css/universe/prepared-universe-runtime.ts",
+ "bytes": 48720,
+ "sha256": "fd0d735b8f44cd639e3c9cf5333a4f4fbef1a2c9dbbbf8d288cf59a4441dae2f"
+ },
+ {
+ "path": "src/renderers/css/dist/universe.js",
+ "bytes": 10185,
+ "sha256": "a4aa3371c6836dcb1df973ab5cadbf9c9bd5d3ebb2555e9e46aff31fc2b9c520"
+ },
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ }
+ ],
+ "mismatches": []
+ },
+ "captureReuse": "Six app views and three-repeat performance measurements remain the 2026-09-20 bb7cab3 evidence. The 25 recorded model, context owner, world-context and runtime entrypoint pins still match. A fresh native browser check verifies the merged catalogue navigation; this is not a new desktop FPS measurement.",
+ "limitations": [
+ "Visual axis-handoff/side-view acceptance is still failed; no new bake or scientific acceptance.",
+ "Minimum zoom is unchanged at 0.05 framing radii.",
+ "The broad 399-test shell sweep and full Lab results remain historical 2026-09-20 runs, not reruns on this merge.",
+ "Six telescope native-decoder checks were not validated locally because the pinned Python environment is absent."
+ ],
+ "sourcePins": [
+ {
+ "path": "tools/prepare-volume-provenance.mts",
+ "bytes": 31380,
+ "sha256": "d923c49fa343ce93d64590dd10b4e40b1f843e306d5e7014144a587815f4518d"
+ },
+ {
+ "path": "site/planet-shell-client.mts",
+ "bytes": 81490,
+ "sha256": "3ad1ee46c0b086b4e901c16c6e45bd619372d2c2b73eada60c0d2498629f74b2"
+ },
+ {
+ "path": "site/catalogue-window.mts",
+ "bytes": 9261,
+ "sha256": "8cace8dfc4766a634a7130904ff55a36d07177f4fbc63e15c2f0f2bf7988e380"
+ },
+ {
+ "path": "site/components/PlanetCatalogueRows.astro",
+ "bytes": 1955,
+ "sha256": "6eb4e18b45723f290ecab5aa2d61ef6201df23fb466992482a91898ea7171c14"
+ },
+ {
+ "path": "site/object-catalog.mts",
+ "bytes": 4439,
+ "sha256": "69335874276d2243c948c9de72dc7346d0a26d250425bc84a1146fdf371d5509"
+ },
+ {
+ "path": "src/objects/m31/runtime-assets.json",
+ "bytes": 15070,
+ "sha256": "2bd4d877481fd744f0ea58a681ccc6f852ecf182ebeafa3a11d920acd1373b9a"
+ },
+ {
+ "path": "src/objects/m33/runtime-assets.json",
+ "bytes": 15713,
+ "sha256": "a5a109a5bb110fa7d16c48ecdbe42713414513a40d4418d86ef78f115025c9fa"
+ },
+ {
+ "path": "src/objects/nearby-universe/runtime-assets.json",
+ "bytes": 713,
+ "sha256": "d76eabec4d44fd1588021a6b4afdffe175b0ff31659efcc898b3f09e12483957"
+ },
+ {
+ "path": "src/objects/nearby-universe/object.json",
+ "bytes": 456,
+ "sha256": "61ea1293df390647595e3ac471ced4cdedcb17befba635cbc7b00e4be80bbbcf"
+ },
+ {
+ "path": "src/objects/nearby-universe/prepared/manifest.json",
+ "bytes": 355,
+ "sha256": "c4fbba552ace7d685a67116f1db52a8a323545e2678a8c7a74615de602a7fe63"
+ }
+ ],
+ "dependentAssets": "M31/M33 provenance changes only its compiler source identity; nearby points retain the previously observed 1454 numeric-only differences (maximum relative4.620e-13), with the incoming authored navigation-source provenance preserved. Three provenance keys were uploaded; the points key was already published."
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/native-focus.json b/src/objects/omega-centauri/evidence/2026-09-21/native-focus.json
new file mode 100644
index 0000000000..6aa486a82e
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/native-focus.json
@@ -0,0 +1,286 @@
+{
+ "base": "http://127.0.0.1:4219",
+ "viewport": {
+ "width": 1440,
+ "height": 1000
+ },
+ "browser": "148.0.7778.96",
+ "inputPins": [
+ {
+ "path": "src/objects/sun/prepared/world-context.json",
+ "bytes": 7536975,
+ "sha256": "4330a638b653f9529d1089b0069353ba8669b7f447c055d7c8b929e1e75b7242"
+ },
+ {
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "bytes": 2633,
+ "sha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991"
+ },
+ {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "bytes": 1202415,
+ "sha256": "e2529a1cc053cc4f79620062d40d05dcf921968d83761289b79010b62655b813"
+ }
+ ],
+ "errors": [],
+ "failed": [],
+ "cold": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "coldContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "warm": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -115235646545839930000
+ ],
+ "orientationXyzw": [
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "distanceM": 8165459600736780000,
+ "distanceRadii": 6.096653162046748,
+ "projectedRadiusPixels": 204.55157392967627
+ },
+ "dragged": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -107690829656048200000,
+ -41068870101575090000,
+ -115681989469650910000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ -4.3973168737309055e-19,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 8165459600736778000,
+ "distanceRadii": 6.096653162046747,
+ "projectedRadiusPixels": 204.55157392967632
+ },
+ "zoomed": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -105161342581436300000,
+ -41827716223958670000,
+ -123337799964711390000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ -4.3973168737309055e-19,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 66966738829483176,
+ "distanceRadii": 0.049999999999986326,
+ "projectedRadiusPixels": 24941.60000000682
+ },
+ "reselected": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -107690829656048270000,
+ -41068870101575070000,
+ -115681989469650940000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ 0,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 8165459600736769000,
+ "distanceRadii": 6.0966531620467395,
+ "projectedRadiusPixels": 204.55157392967655
+ },
+ "reselectedContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "zoomedOut": {
+ "world": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "pose": {
+ "positionM": [
+ -146973272045458080000,
+ -29284137384752270000,
+ 3211258784085824000
+ ],
+ "orientationXyzw": [
+ -0.04750474789854394,
+ -0.158349159661813,
+ 0,
+ 0.9862397490273314
+ ]
+ }
+ },
+ "distanceM": 133933478928421720000,
+ "distanceRadii": 100.00000094779793,
+ "projectedRadiusPixels": 12.470799881802016
+ },
+ "farContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 0.464877
+ },
+ {
+ "id": "lmc",
+ "display": "block",
+ "opacity": 0.999838
+ },
+ {
+ "id": "m8",
+ "display": "block",
+ "opacity": 0.0740891
+ },
+ {
+ "id": "m42",
+ "display": "none",
+ "opacity": 0
+ },
+ {
+ "id": "smc",
+ "display": "block",
+ "opacity": 0.999838
+ }
+ ],
+ "overview": [
+ {
+ "display": "block",
+ "opacity": 0.534735
+ }
+ ]
+ },
+ "returnedContext": {
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": 1
+ },
+ {
+ "id": "m8",
+ "display": "none",
+ "opacity": 0
+ },
+ {
+ "id": "smc",
+ "display": "none",
+ "opacity": 0
+ }
+ ],
+ "overview": [
+ {
+ "display": "none",
+ "opacity": 0
+ }
+ ]
+ },
+ "rendering": {
+ "totalElements": 23210,
+ "volumes": [
+ {
+ "id": "omega-centauri",
+ "display": "block",
+ "opacity": "1",
+ "elements": 1422,
+ "planes": 1377,
+ "stars": 0
+ },
+ {
+ "id": "m8",
+ "display": "none",
+ "opacity": "0",
+ "elements": 2290,
+ "planes": 2019,
+ "stars": 226
+ },
+ {
+ "id": "smc",
+ "display": "none",
+ "opacity": "0",
+ "elements": 2635,
+ "planes": 816,
+ "stars": 1803
+ }
+ ],
+ "galaxyOverview": [
+ {
+ "opacity": "1",
+ "display": "none",
+ "elements": 1382
+ }
+ ]
+ },
+ "result": "passed"
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/telescope-environment.json b/src/objects/omega-centauri/evidence/2026-09-21/telescope-environment.json
new file mode 100644
index 0000000000..2d5b823fc1
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/telescope-environment.json
@@ -0,0 +1,23 @@
+{
+ "purpose": "Actual existing-fixture import diagnostics; not a replacement test or mocked decoder.",
+ "results": [
+ {
+ "fixture": "tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "family": "F02",
+ "descriptorPresent": false,
+ "issue": {
+ "state": "unsupported",
+ "reason": "The selected astropy-mixed-nd-fits@1 content validator refused the imported bytes: The astronomy packages are not installed: node tools/objects/astronomy-packages/toolchain.mts install"
+ }
+ },
+ {
+ "fixture": "tests/fixtures/telescope-families/f14-healpix/bayestar.fits.gz",
+ "family": "F14",
+ "descriptorPresent": false,
+ "issue": {
+ "state": "unsupported",
+ "reason": "The selected astropy-healpix-fits@1 content validator refused the imported bytes: The astronomy packages are not installed: node tools/objects/astronomy-packages/toolchain.mts install"
+ }
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/evidence/2026-09-21/telescope-types.json b/src/objects/omega-centauri/evidence/2026-09-21/telescope-types.json
new file mode 100644
index 0000000000..46557a3d2f
--- /dev/null
+++ b/src/objects/omega-centauri/evidence/2026-09-21/telescope-types.json
@@ -0,0 +1,155 @@
+{
+ "task": "Incoming-main telescope typing cleanup",
+ "round": 1,
+ "maximumCleanupRounds": 2,
+ "head": "bb7cab3c2e5be0b58a63e2cbeca33ca94a7b3b5a",
+ "incoming": "3aebc31094dff8332755b394b88c708b7c3f1062",
+ "files": [
+ {
+ "path": "tools/objects/telescopes/families/f16-spatial-physical.mts",
+ "incomingSha256": "d217fdf190150c814c0a214ebd868a939cf0db04385ab0ee49485d17b0564427",
+ "stagedEqualsIncoming": true,
+ "modifiedSha256": "635e1b6e6bea9e3753267aebee52c1a30e5d66f08a12caf2fd7600a727508fb0",
+ "bytes": 5507
+ },
+ {
+ "path": "tools/objects/telescopes/family-operation.mts",
+ "incomingSha256": "1955b11e7df9a97452e5e74b44ab6200e1b7e9e7ca639b5c1abe8946ce47d05f",
+ "stagedEqualsIncoming": true,
+ "modifiedSha256": "b4ef99f14b55d6f43dcd032fa45bd5cb3ef0ac29162d00e0370ac10dc8c7d06b",
+ "bytes": 56027
+ },
+ {
+ "path": "tools/objects/telescopes/family-request.mts",
+ "incomingSha256": "102b894f46a4af05f3c7365d27c24af740d64b6ed99c28b8385281649af7fadb",
+ "stagedEqualsIncoming": true,
+ "modifiedSha256": "7a030a9ea8013200647bffba2410947a02d22c9653abb1acb9837add41f7a9dd",
+ "bytes": 8835
+ },
+ {
+ "path": "tools/objects/telescopes/families/f14-healpix.test.mts",
+ "incomingSha256": "c7e1bbcad9a69cbf98ed1ad24370f389025a1345af032ec1018880c76696f012",
+ "stagedEqualsIncoming": true,
+ "modifiedSha256": "bb9bd4a6a1b9f939e1190c9eee7d0cf20a59a54c186a3c4362c869790ec4c521",
+ "bytes": 3768
+ },
+ {
+ "path": "tools/objects/telescopes/local-import.test.mts",
+ "incomingSha256": "744d26a31d0545e83fe1165a347650e8b912ed1d8a1a3f68bd873fe64ef41139",
+ "stagedEqualsIncoming": true,
+ "modifiedSha256": "e7991ab4b9984a618ed782be45f2fb93512f53c4ef5d580041e0b80e956009c0",
+ "bytes": 6659
+ },
+ {
+ "path": "tools/objects/telescopes/session.test.mts",
+ "incomingSha256": "3084e630527243cf9ba37216ff3e8fdcfc78c00dfd14694d46d738dcbf8e9992",
+ "stagedEqualsIncoming": true,
+ "modifiedSha256": "482ea175375730473543d4dbb35fda7711a5d4d90bf3608dbb806578e16f365a",
+ "bytes": 12789
+ },
+ {
+ "path": "tools/objects/telescopes/vo/session.test.mts",
+ "incomingSha256": "666e2a00fb5d02c5e7759111d58072a517736ecb420a0601ccc2455d622e6008",
+ "stagedEqualsIncoming": true,
+ "modifiedSha256": "f11ab27f7c321e6db1acc5f8e7c4f03569b219184ec1c9bf99348744944fabd9",
+ "bytes": 11819
+ }
+ ],
+ "changeSummary": "Three source type fixes; four existing test narrowings. No new casts or validation bypasses. F16 array const extraction preserves existing operation values. No runtime source algorithm change.",
+ "checks": [
+ {
+ "command": "pnpm typecheck:tools",
+ "exitCode": 0,
+ "status": "pass"
+ },
+ {
+ "command": "pnpm typecheck:tests",
+ "exitCode": 0,
+ "status": "pass",
+ "typedFiles": 1758
+ },
+ {
+ "command": "node --test tools/objects/telescopes/families/f16-spatial-physical.test.mts tools/objects/telescopes/families/f14-healpix.test.mts tools/objects/telescopes/family-request.test.mts tools/objects/telescopes/family-request-session.test.mts tools/objects/telescopes/local-import.test.mts tools/objects/telescopes/session.test.mts tools/objects/telescopes/vo/session.test.mts",
+ "exitCode": 1,
+ "tests": 26,
+ "passed": 21,
+ "failed": 5,
+ "skipped": 0
+ },
+ {
+ "command": "node --test --test-name-pattern='F07 public selection|F12 diagnostics and F16|public execution view' tools/objects/telescopes/family-operation.test.mts",
+ "exitCode": 1,
+ "tests": 3,
+ "passed": 2,
+ "failed": 1,
+ "skipped": 0
+ },
+ {
+ "command": "git diff --check",
+ "exitCode": 0,
+ "status": "pass"
+ }
+ ],
+ "behaviorTotal": {
+ "tests": 29,
+ "passed": 23,
+ "environmentBlocked": 6,
+ "otherFailures": 0
+ },
+ "blockedTests": [
+ {
+ "name": "F14 runs the pinned Bayestar IMPLICIT vector column through public selection and product readback",
+ "observedError": "The astronomy packages are not installed: node tools/objects/astronomy-packages/toolchain.mts install"
+ },
+ {
+ "name": "a real supported local FITS crosses import, outputs, family export and product readback without a hand-authored descriptor",
+ "observedError": "AssertionError: actual undefined; expected '/var/folders/hr/wf3y2_yx3xxdgk_j_0lfgt6r0000gn/T/telescope-import-family-WhGv4D/imported/descriptor.json'",
+ "attribution": "Actual repeat of the same fixture records astropy-mixed-nd-fits@1 validator refusal with the missing astronomy packages error; descriptor is omitted."
+ },
+ {
+ "name": "local HEALPix import reuses its Astropy-owned descriptor and operations",
+ "observedError": "AssertionError: actual undefined; expected 'descriptor.json'",
+ "attribution": "Actual repeat of the same fixture records astropy-healpix-fits@1 validator refusal with the missing astronomy packages error; descriptor is omitted."
+ },
+ {
+ "name": "saved VO choice acquires, qualifies, exports through existing owners, and refuses tampered evidence",
+ "observedError": "The astronomy packages are not installed: node tools/objects/astronomy-packages/toolchain.mts install"
+ },
+ {
+ "name": "two saved SODA subsets of one parent keep separate acquisition, qualification, and delivery records",
+ "observedError": "The astronomy packages are not installed: node tools/objects/astronomy-packages/toolchain.mts install"
+ },
+ {
+ "name": "F07 public selection runs the pinned cdflib owner and writes native coordinates",
+ "observedError": "The astronomy packages are not installed: node tools/objects/astronomy-packages/toolchain.mts install"
+ }
+ ],
+ "artifacts": [
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round1-tools-types.log",
+ "bytes": 92,
+ "sha256": "544f8b5e19255449ca30882644aead090d6d3493015fc6c6452d6e35b998d32d"
+ },
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round1-test-types.log",
+ "bytes": 162,
+ "sha256": "de92610b441d6a18f51972d19d9f736c3951f906e91ca4163fd24faa87e52502"
+ },
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round1-behavior.log",
+ "bytes": 10618,
+ "sha256": "6498565db66098a58e06bbeb777e660c258bf33551e3dbc0c27f40997f08284d"
+ },
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round1-dispatch.log",
+ "bytes": 1939,
+ "sha256": "242fa8b1a61d2956e9a08c7cc058dae120cd3fd7b11740057825bae2de279ead"
+ },
+ {
+ "path": "output/pr390-audit/ci-head-bb7cab3/telescope-cleanup-round1-environment-attribution.json",
+ "bytes": 971,
+ "sha256": "0accaba4f244d4b668fccb3cf8a81b8fa872eb5db1070fad176afeb41c452c79"
+ }
+ ],
+ "limitations": "The six tests did not complete their native assertions; none is reported passing. No astronomy toolchain installed, no mock substitution, no waived gates. Independent final review belongs to pr380/root."
+}
diff --git a/src/objects/omega-centauri/investigations.json b/src/objects/omega-centauri/investigations.json
new file mode 100644
index 0000000000..3f22fe99a2
--- /dev/null
+++ b/src/objects/omega-centauri/investigations.json
@@ -0,0 +1,160 @@
+{
+ "schema": "cssearth-investigation-ledger@1",
+ "objectId": "omega-centauri",
+ "entries": [
+ {
+ "id": "lens-eso1119b",
+ "subject": "VST/OmegaCAM integrated optical light",
+ "status": "included",
+ "finding": "Native publisher TIFF and AVM are pinned. The selected adaptive result 7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549 replays into the local shared application bank with 459 retained planes in this lens and zero separate stars. This is an application integration candidate, not a passed visual result. The VST X/Z and Y/Z normalized L1 are 0.120079 and 0.110174, above the unchanged 0.04 gate. Earlier bake 1 was rejected for bright clumps; bake 2 stopped during painting; bake 3 completed after the roundoff fix but retained the failed neutral geometry. Included means selected for this draft app integration. The checked revision pins the retained Lab measurement, not the uncommitted app package or visual acceptance; the delivery-blocker entry retains that unresolved gate.",
+ "evidence": [
+ "https://www.eso.org/public/images/eso1119b/",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-handoffs.json",
+ "https://github.com/layoutit/css.earth/pull/390"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ]
+ },
+ {
+ "id": "lens-eso0844a",
+ "subject": "WFI optical material on common geometry",
+ "status": "included",
+ "finding": "The WFI observation recolors the same geometry over its smaller footprint; publisher DSS-filled gaps remain explicit. Selected adaptive result 7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549 replays into the local application bank with 459 retained planes in this lens and no duplicate stars. Its X/Z and Y/Z normalized L1 are 0.123877 and 0.113697, above the unchanged 0.04 gate. Successful material painting and resource reproduction do not establish visual acceptance. Included means selected for this draft app integration. The checked revision pins the retained Lab measurement, not the uncommitted app package or visual acceptance; the delivery-blocker entry retains that unresolved gate.",
+ "evidence": [
+ "https://www.eso.org/public/images/eso0844a/",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-handoffs.json",
+ "https://github.com/layoutit/css.earth/pull/390"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ]
+ },
+ {
+ "id": "published-mge",
+ "subject": "Published projected density and oblate depth prior",
+ "status": "included",
+ "finding": "The complete D’Souza & Rix (2013) table was read and all eight Gaussian coefficients transcribed. PA 100 degrees and inclination 50 degrees define the selected conditional model; mirror tilt and six-sigma cutoff are authored. The current two-scale candidate uses envelope fraction 0.95, 8-pixel smoothing and at most 4096 finite residual features. Its 512-pixel light fit is not measured stellar depth or a resolved-star catalogue.",
+ "evidence": [
+ "https://doi.org/10.1093/mnras/sts426",
+ "https://arxiv.org/pdf/astro-ph/0509228",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/physical-evidence.json"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ]
+ },
+ {
+ "id": "cluster-distance",
+ "subject": "Combined cluster distance",
+ "status": "included",
+ "finding": "Baumgardt & Vasiliev (2021) Table 2 was downloaded in full and gives 5426 ± 47 pc for NGC 5139. That common scale does not locate individual stars.",
+ "evidence": [
+ "https://doi.org/10.1093/mnras/stab1474",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/physical-evidence.json"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ]
+ },
+ {
+ "id": "king-abel",
+ "subject": "Historical spherical King-Abel approximation",
+ "status": "excluded",
+ "finding": "Retained historical profile substituted a 3D dynamical core radius for a projected radius and converted half-mass to half-light by approximation. It is not the selected MGE prior.",
+ "evidence": [
+ "https://doi.org/10.1093/mnras/sts426",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/physical-evidence.json"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ],
+ "revisitWhen": "A distinct sourced comparison would need an appropriate photometric fit and explicit shape assumptions."
+ },
+ {
+ "id": "wilson-fit",
+ "subject": "McLaughlin and van der Marel Wilson alternative",
+ "status": "deferred",
+ "finding": "Actual NGC 5139 CDS rows were retrieved: Wilson W0 = 4.70 and scale 196.810 arcsec fit those data better than King (1966). The current MGE already supplies radial flattening and analytic deprojection.",
+ "evidence": [
+ "https://cdsarc.cds.unistra.fr/ftp/J/ApJS/161/304/table10.dat",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/physical-evidence.json"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ],
+ "revisitWhen": "A specified model comparison justifies implementing the correct Wilson distribution-function solution."
+ },
+ {
+ "id": "hst-registration-companion",
+ "subject": "oMEGACat II HST WCS companion",
+ "status": "deferred",
+ "finding": "Author release metadata and the real F625W FITS header were inspected. The full 1.6 GB science array was not acquired. Central coverage alone cannot validate outer-field distortion.",
+ "evidence": [
+ "https://zenodo.org/records/11104046",
+ "https://arxiv.org/html/2404.03722v2",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/physical-evidence.json"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ],
+ "revisitWhen": "Independent absolute astrometry is required beyond the existing relative native-image registration."
+ },
+ {
+ "id": "modern-inclination",
+ "subject": "Newer kinematic orientation",
+ "status": "deferred",
+ "finding": "Accepted oMEGACat II v2 reports 43.9 ± 1.3 degrees and PA 104 ± 1 degree. Its inclination interval crosses the older MGE deprojection limit 43.603810 degrees, so it is retained as a competing constraint.",
+ "evidence": [
+ "https://arxiv.org/html/2404.03722v2",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/physical-evidence.json"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ],
+ "revisitWhen": "A coherent photometric/kinematic refit supports combining the newer inclination with a compatible projected profile."
+ },
+ {
+ "id": "delivery-blocker",
+ "subject": "459-layer application candidate and failed visual handoff",
+ "status": "unresolved",
+ "finding": "Selected result 7a4f8781a7d19fafa57f11e65eae6ee24975082d3fcae0c1644cbdd68a5ac549 has 459 retained XYZ slabs per bank, reduced from 779. Actual application preparation completed on 2026-09-20 using code at 862cdbed96bbca43cdf1f13549c94e4ca501add3 plus the uncommitted object package and scoped provenance selector. All 1,377 expected raw resource identities matched before packing two optical lenses into 29 resources each; prepared-assets.json pins the current delivery bytes, including generated metadata. The compact source is 2,277,027 bytes, SHA-256 869e9ab24b644c4ea7d04cf2d4e2c719445c205b879eaebf13a420c7d7d75eac. The legacy acceptedLabResult field identifies the selected source only; qualification remains app-integration-candidate/failed. Neutral X/Z and Y/Z normalized L1 are 0.12498652413707272 and 0.1146546907233652 versus 0.04; neutral X/Y brightness difference is 0.05648728604634782 versus 0.05. Both optical lenses also fail image stability. Controlled-camera main-app inspection passed seven captures across both lenses and both side axes, with no page errors or failing requests, one scene/camera and no canvas. That inspection applied camera poses after mounting. A separate native check now closes the arrival defect: cold and warm routes automatically frame Omega at 204.5516 pixels projected radius in a 1440 × 1000 viewport, and real drag, wheel and reselection work without injected inspection poses, with no page errors or failed requests. The check includes the subsequent uncommitted shared focus fix. The subsequent shared context-visibility fix passes the native browser regression: only Omega mounts at cold arrival, the detailed Milky Way is hidden, real wheel input to 100 framing radii restores surrounding models, and reselection hides them again. Surrounding detail fades from zero through eight radii to full at 32, without changing the camera or original Sun-distance sky contribution. A preceding page-only visibility control reduced an identical drag from 33.06 to 8.38 seconds in SwiftShader; this isolates a background rendering burden, not desktop GPU FPS. The wheel check reaches the unchanged 0.05-framing-radius minimum (2.17 pc); acceptable close-up presentation remains unresolved, separately from the corrected arrival framing. A cold compact replay matched all 1,377 expected raw resource identities and 60 delivery files in 218.60 seconds. R2 restoration installed and hash-verified all 64 assets into an empty target; the warm repeat reused all 64. Local retained receipts: evidence/2026-09-20/app-inspection.json, native-focus.json, cold-replay.json, r2-restore.json and validation.json. These delivery results do not close the visual gate or establish a release/merge. The checked revision pins the failed Lab handoff measurement. It does not contain this application package; source/delivery.json and prepared-assets.json separately identify the current uncommitted app candidate.",
+ "evidence": [
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-handoffs.json",
+ "https://github.com/layoutit/css.earth/blob/862cdbed96bbca43cdf1f13549c94e4ca501add3/labs/nebula/models/omega-centauri/evidence/2026-09-20/layer-bake.json",
+ "https://github.com/layoutit/css.earth/pull/390"
+ ],
+ "checked": [
+ {
+ "date": "2026-09-20",
+ "commit": "862cdbed96bbca43cdf1f13549c94e4ca501add3"
+ }
+ ],
+ "revisitWhen": "The unchanged actual-renderer handoff gate and main-app visual acceptance pass, including acceptable close-zoom presentation. Navigation, context visibility, cold replay and R2 restoration do not waive these requirements."
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/object.json b/src/objects/omega-centauri/object.json
new file mode 100644
index 0000000000..c5004d3e5d
--- /dev/null
+++ b/src/objects/omega-centauri/object.json
@@ -0,0 +1,44 @@
+{
+ "schema": "cssearth-object@1",
+ "id": "omega-centauri",
+ "type": "volume-lens-bank",
+ "properties": {
+ "frame": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5,
+ "originM": [
+ -105005412559826930000,
+ -41769153670092810000,
+ -123207430757263850000
+ ],
+ "localToReferenceXyzw": [
+ 0.7712350124749122,
+ -0.5231773789172258,
+ -0.20356010658195295,
+ 0.3000754383991924
+ ],
+ "metersPerUnit": 811718046418199.9,
+ "boundsUnits": {
+ "min": [
+ -2190.705034159304,
+ -2194.8969278075674,
+ -4259.5353966530165
+ ],
+ "max": [
+ 2190.705034159304,
+ 2194.8969278075674,
+ 4259.5353966530165
+ ]
+ }
+ },
+ "preparation": {
+ "source": "source/delivery.json",
+ "sha256": "d1e34564e98eae6ebe9d8d59b8cfd4ebe19c20e0c14f8279c72444f96faf36a5"
+ }
+ },
+ "prepared": {
+ "format": "cssearth-volume-lenses@1",
+ "url": "prepared/lenses.json",
+ "sha256": "42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c"
+ }
+}
diff --git a/src/objects/omega-centauri/prepared-assets.json b/src/objects/omega-centauri/prepared-assets.json
new file mode 100644
index 0000000000..59f08408fa
--- /dev/null
+++ b/src/objects/omega-centauri/prepared-assets.json
@@ -0,0 +1,316 @@
+{
+ "schema": "cssomega-centauri-prepared-assets@1",
+ "resourceRoot": "prepared",
+ "assets": [
+ {
+ "filename": "delivery.json",
+ "bytes": 695,
+ "sha256": "b62c0d4a8203fc424c1224ba7fb6a2c780f98abc25da2d3bd95e17d0145ec2ba"
+ },
+ {
+ "filename": "eso0844a/atlases/x.webp",
+ "bytes": 630850,
+ "sha256": "2be14d24b7b8c9207982a44d0257d2d33e9e2f4c6f1a20e11e8e83a2d419ff3e"
+ },
+ {
+ "filename": "eso0844a/atlases/y.webp",
+ "bytes": 588746,
+ "sha256": "f378d5167d5c82e2779c3507a67f5f3cc7d3cd67e8396a70a5e8d47d0bca3247"
+ },
+ {
+ "filename": "eso0844a/atlases/z.webp",
+ "bytes": 619926,
+ "sha256": "f2cfe0f147d8dd07d66be1acb6442c9898e52a37426ed332818e704111ba2a6a"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00n.png",
+ "bytes": 12813,
+ "sha256": "4156c57dd44f146b3de1b146f31da37f676ef5ca97c388fc03d3442064c3fcae"
+ },
+ {
+ "filename": "eso0844a/impostors/view-00p.png",
+ "bytes": 12843,
+ "sha256": "7827406e40fef2a444bf1da7c92dbb73aea22132dc4da142cbddaa1c3038b954"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0n0.png",
+ "bytes": 14310,
+ "sha256": "e9d6d2605bd483e58d3ff86bb49f010a0dbb3e88bd19a7529f56623288d5bb90"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0nn.png",
+ "bytes": 16252,
+ "sha256": "26161006830d714bee7e57dc84aa2bde9a22fb2d375d32188231d5b9fe6ca1d0"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0np.png",
+ "bytes": 16701,
+ "sha256": "089d623a473e4fa400428e882ed675afdfe55fa15d05c0dc05879dc5279f234c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0p0.png",
+ "bytes": 14252,
+ "sha256": "3612a4bb86230a87d514ae5417aa57300768b1a10d61f482414dfae1d4806da9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pn.png",
+ "bytes": 16654,
+ "sha256": "c8621057e346b5167a188ece82721e947bc5d06d358edc6883ab2ec933925309"
+ },
+ {
+ "filename": "eso0844a/impostors/view-0pp.png",
+ "bytes": 16266,
+ "sha256": "efbc1796cbeb321201318f8be76bf2f4bcb45491d99710180e1fc4566b271963"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n00.png",
+ "bytes": 13873,
+ "sha256": "aff1a64e71cdca04cbe72170feaa11e5284c57f2479703250981c7cd3510f1af"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0n.png",
+ "bytes": 16245,
+ "sha256": "1413954ca8edc01593679686edf534a55785207dd56ecbdbdd2a1f81bf1c6889"
+ },
+ {
+ "filename": "eso0844a/impostors/view-n0p.png",
+ "bytes": 16103,
+ "sha256": "aa614dd95fa6c33dc9650e91599b75a2ba7b634e884e43fc95f076dd198a850c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nn0.png",
+ "bytes": 17181,
+ "sha256": "8211b8045e0eddd5d15ba737cde7ccf4373cd19cd322b71c227e9db9ac29b85c"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnn.png",
+ "bytes": 18333,
+ "sha256": "fd456e584c2d55ba70bb82c31ebdae54bd6050c52a1a5d77a1ae5a4e30be0d15"
+ },
+ {
+ "filename": "eso0844a/impostors/view-nnp.png",
+ "bytes": 18205,
+ "sha256": "4ac7ba2ab1c61280585304de0b1b343b2ce6d26177b2c830e4cca903d941dd21"
+ },
+ {
+ "filename": "eso0844a/impostors/view-np0.png",
+ "bytes": 16566,
+ "sha256": "f08f763fa3b91d78b5471c4c92f9c9eee005f730d0f135dd2ed3c946a3103072"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npn.png",
+ "bytes": 18179,
+ "sha256": "d4777b2ba664846faca602046e708e817d3e620617b3708029444f37520a979d"
+ },
+ {
+ "filename": "eso0844a/impostors/view-npp.png",
+ "bytes": 17882,
+ "sha256": "1555e695cd4472ef5c8040e42f5af64b7adb30d5d48d9fd1640bd7611f490e0b"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p00.png",
+ "bytes": 13926,
+ "sha256": "4b3d0c279484f7fd46295037a60e41e081dd952ee23dfc4da54b2b3ea95c60c5"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0n.png",
+ "bytes": 16082,
+ "sha256": "4a8d2219e7753680c2a62ac94540737d38dace55b010179413c586a4d32a6dc9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-p0p.png",
+ "bytes": 16222,
+ "sha256": "42b8d608f6fce88844edebdc71820eb00a78bdb759eece0234be531ec242e189"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pn0.png",
+ "bytes": 16602,
+ "sha256": "05407bb1fe5bcafe2d1657f39c930055183bfd7aea3e7ec29cf1a4b6864f08e8"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnn.png",
+ "bytes": 17957,
+ "sha256": "5d3dff6ed46bdd9d02b524036cf15e54cb3235377e87f1092a9001425e5bac71"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pnp.png",
+ "bytes": 18142,
+ "sha256": "04cc74f6a9e2120398b8c0d3804f175378e2dd50ca8e9e2c6c19f115a9bc81f4"
+ },
+ {
+ "filename": "eso0844a/impostors/view-pp0.png",
+ "bytes": 17201,
+ "sha256": "e852ac472d7d640d6fbb134873e7cdd30d3ffbbc6a1560672c3c5d5ce9276fa9"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppn.png",
+ "bytes": 18148,
+ "sha256": "394139a551defd9c14085f8e9177a1d106ec12de61724a855551462df3088259"
+ },
+ {
+ "filename": "eso0844a/impostors/view-ppp.png",
+ "bytes": 18310,
+ "sha256": "6e54542765e0daf109a7a93de5013efc0407eadd3c66139b7598598cff165349"
+ },
+ {
+ "filename": "eso1119b/atlases/x.webp",
+ "bytes": 717392,
+ "sha256": "7c140e5355dae4ee747b5033fbebbeb1e3065a60fbfab907c9a497c195c36ca8"
+ },
+ {
+ "filename": "eso1119b/atlases/y.webp",
+ "bytes": 665178,
+ "sha256": "b4303f0b696ebe2dcacc4d29cd16c076963e64ce9b83e6baaccc6d799c3d24d5"
+ },
+ {
+ "filename": "eso1119b/atlases/z.webp",
+ "bytes": 744376,
+ "sha256": "48cabfce2c49e49af9c1dcfc909279bda7c29e93e4c3dcee52bc4a1621281e62"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00n.png",
+ "bytes": 17819,
+ "sha256": "fc4b123e3af4cfd234ce5c9f0d0152f15332a11d856dc39b64d6b2ec99e33cff"
+ },
+ {
+ "filename": "eso1119b/impostors/view-00p.png",
+ "bytes": 17808,
+ "sha256": "b3c41a3bc5e4b2e455a46a5f9b45a55f73818c9dd81492c0bf13f5c470a1c1cc"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0n0.png",
+ "bytes": 18194,
+ "sha256": "98795bd3eb6fff2b6725005a2d27ed77e956c314d0f5fbf8c49be78ffa42d1b6"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0nn.png",
+ "bytes": 20855,
+ "sha256": "3d3492f4abe86ed315bef89b4eed6ea12dde6e42a21127dde8dbd472afa4c09f"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0np.png",
+ "bytes": 21992,
+ "sha256": "d9becac304218245c0a81feb451e4938fa2738cee115795999110a1f3496608c"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0p0.png",
+ "bytes": 18135,
+ "sha256": "5c1c63b173cc7d66476ddb33bff80542a66b0ff5ae550aa54e1ce34c281fc2a3"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pn.png",
+ "bytes": 22003,
+ "sha256": "6b049f9dfddf5fcfa7b588776192479ff9e5f3dec6eba7e8b55729bcbcf11a77"
+ },
+ {
+ "filename": "eso1119b/impostors/view-0pp.png",
+ "bytes": 20827,
+ "sha256": "60e6c04a4bd0a75528ba788d6dfd0b765cf016d64eb317a30c277c329e0479fe"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n00.png",
+ "bytes": 18428,
+ "sha256": "bdbb5b335df7562f066a9b783f34af007123779136f787a6fe1e48d876df3f81"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0n.png",
+ "bytes": 21672,
+ "sha256": "b8374e6d60df50e5dfbd74b93e7afc720637052021a2c39498d7f679f0acc741"
+ },
+ {
+ "filename": "eso1119b/impostors/view-n0p.png",
+ "bytes": 21114,
+ "sha256": "17bafffbcf235dc9467ac0bc466d5190b43b376dda6a617fa83733d8ff52264d"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nn0.png",
+ "bytes": 20813,
+ "sha256": "04574df58cc0371bcd9cbc504802abb32602736aca5ed78723c31e229c0fe3d2"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnn.png",
+ "bytes": 23385,
+ "sha256": "ba8db676f74fc73640634e80224852b89120b7458847d11e58ef6b4c6b58bb74"
+ },
+ {
+ "filename": "eso1119b/impostors/view-nnp.png",
+ "bytes": 23516,
+ "sha256": "59b45a749a76bc20b941251a563ab8f1ea41f6b25f6bb0d674308b09d597f729"
+ },
+ {
+ "filename": "eso1119b/impostors/view-np0.png",
+ "bytes": 20729,
+ "sha256": "644dc71d5c613fdbd1faf5ad662cc88572d8251308b5c62b0f9d8bc93c17d4fd"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npn.png",
+ "bytes": 24083,
+ "sha256": "2f256ea4a133442ce5dfdb564999c92e22825d3ecb4aa7964ebc120c4450c64c"
+ },
+ {
+ "filename": "eso1119b/impostors/view-npp.png",
+ "bytes": 22900,
+ "sha256": "810e0f41aec1f714a15292a78560e1b8af8e21e4b6830b31ad5278d4092541a5"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p00.png",
+ "bytes": 18397,
+ "sha256": "eafbf2d83cf5d90187c429b8f2691aee84b8e8e1707f007398e6b38819982285"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0n.png",
+ "bytes": 21097,
+ "sha256": "50cef00922fc06c8b371031eb49d0b649b30973132272a2c1825cbe5a58536d8"
+ },
+ {
+ "filename": "eso1119b/impostors/view-p0p.png",
+ "bytes": 21643,
+ "sha256": "36898320964881289e223386a14a96f03c496db91828a226e2bee0f108c521a8"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pn0.png",
+ "bytes": 20712,
+ "sha256": "fc48f7402e52da24ceb72e20835c5f3721f2121d5cb346b1e9f57bd34258af7e"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnn.png",
+ "bytes": 22866,
+ "sha256": "b32298d848102bc899c2db9254ff43fb0d5716aaf074b7a3a003580ab6638610"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pnp.png",
+ "bytes": 24053,
+ "sha256": "97fd71da3b4379b327435b54360bd950128998a28969d91576f19d64fb150b19"
+ },
+ {
+ "filename": "eso1119b/impostors/view-pp0.png",
+ "bytes": 20832,
+ "sha256": "d155d4807b4efe66de898bc8863be49d9763c03629bbbf47a6b04c0fe82e29ed"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppn.png",
+ "bytes": 23507,
+ "sha256": "b16791ed70b6ca22bbd84ce8d9a295711c1b55cc8e1dd28dab6e968d535b9788"
+ },
+ {
+ "filename": "eso1119b/impostors/view-ppp.png",
+ "bytes": 23343,
+ "sha256": "cd8a0f7a94149a386a8b98ec8a47257539193ef9f612701309e32cc6def08212"
+ },
+ {
+ "filename": "lenses.json",
+ "bytes": 433153,
+ "sha256": "42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c"
+ },
+ {
+ "filename": "presentation.json",
+ "bytes": 4200,
+ "sha256": "69e809ee3e10109b51e9e3a2e8f159436a7038a43693e8820da396a90e0c53cc"
+ },
+ {
+ "filename": "provenance.json",
+ "bytes": 76924,
+ "sha256": "974bcfa338298a83ac0190a5668bf4e4d377bb5fbe90e93e04b27afaf256ffd9"
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/runtime-assets.json b/src/objects/omega-centauri/runtime-assets.json
new file mode 100644
index 0000000000..6c5126f46c
--- /dev/null
+++ b/src/objects/omega-centauri/runtime-assets.json
@@ -0,0 +1,18 @@
+{
+ "schema": "cssomega-centauri-runtime-assets@1",
+ "resourceRoot": "prepared",
+ "assets": [
+ {
+ "filename": "datasets/8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb.webp",
+ "location": "public",
+ "bytes": 187756,
+ "sha256": "8194709130c68dd847ebb507fe56b7f0c0d955b322ccdbba3b1a4f8e2e6f88eb"
+ },
+ {
+ "filename": "datasets/33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1.webp",
+ "location": "public",
+ "bytes": 218816,
+ "sha256": "33c65ca68df0edcf5fda077fdcf17fb78d72214dc8774614dd92a82475bf2cb1"
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/source/bake-inputs.json.gz b/src/objects/omega-centauri/source/bake-inputs.json.gz
new file mode 100644
index 0000000000..ee7b118c79
Binary files /dev/null and b/src/objects/omega-centauri/source/bake-inputs.json.gz differ
diff --git a/src/objects/omega-centauri/source/bake-inputs/references/01-compiler.json b/src/objects/omega-centauri/source/bake-inputs/references/01-compiler.json
new file mode 100644
index 0000000000..1acd5bdc90
--- /dev/null
+++ b/src/objects/omega-centauri/source/bake-inputs/references/01-compiler.json
@@ -0,0 +1,15 @@
+{
+ "schema": "cssearth-nebula-compiler@1",
+ "id": "omega-centauri",
+ "label": "Omega Centauri",
+ "observationRecipe": "labs/nebula/models/omega-centauri/observations.json",
+ "observationCatalogue": ".local/nebula-lab/observations/omega-centauri/observations.json",
+ "structureRecipe": "labs/nebula/models/omega-centauri/observation-structures.json",
+ "structureCatalogue": ".local/nebula-lab/observations/omega-centauri/structures/catalogue.json",
+ "photometricPriorRecipe": "labs/nebula/models/omega-centauri/photometric-mge.json",
+ "defaultSourceId": "eso1119b",
+ "sourceWeights": { "eso1119b": 1, "eso0844a": 0 },
+ "defaultControls": { "detail": 1, "faint": 0.35, "depth": 1 },
+ "maximumStars": 0,
+ "interpretation": "The published oblate eight-Gaussian surface-brightness model supplies a conditional three-dimensional starlight prior at its selected inclination and position angle. Registered VST light fits finite emission components within that prior; the independent WFI image recolours the same geometry. Individual feature depths, finite supports and near/far tilt are conditional display choices, not measured stellar positions or membership. Integrated stellar light is preserved; no duplicate compact-star catalogue is added. This is relative display emission, not calibrated luminosity, gas density or a recovered dynamical model."
+}
diff --git a/src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json b/src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json
new file mode 100644
index 0000000000..22a4e749c4
--- /dev/null
+++ b/src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json
@@ -0,0 +1,76 @@
+{
+ "schema": "cssearth-photometric-mge@1",
+ "id": "omega-centauri",
+ "centerIcrsDegrees": [
+ 201.696958,
+ -47.479539
+ ],
+ "distancePc": 5426,
+ "positionAngleEastOfNorthDegrees": 100,
+ "inclinationDegrees": 50,
+ "lineOfSightTiltSign": 1,
+ "cutoffSigma": 6,
+ "gaussians": [
+ {
+ "centralAmplitude": 1290.195,
+ "sigmaArcsec": 28.5342,
+ "projectedAxisRatio": 1
+ },
+ {
+ "centralAmplitude": 4662.587,
+ "sigmaArcsec": 115.88586,
+ "projectedAxisRatio": 0.9991714
+ },
+ {
+ "centralAmplitude": 2637.784,
+ "sigmaArcsec": 150.8031,
+ "projectedAxisRatio": 0.7799464
+ },
+ {
+ "centralAmplitude": 759.8591,
+ "sigmaArcsec": 212.20356,
+ "projectedAxisRatio": 0.724126
+ },
+ {
+ "centralAmplitude": 976.0853,
+ "sigmaArcsec": 324.22368,
+ "projectedAxisRatio": 0.8556435
+ },
+ {
+ "centralAmplitude": 195.4156,
+ "sigmaArcsec": 538.98336,
+ "projectedAxisRatio": 0.9392021
+ },
+ {
+ "centralAmplitude": 38.40327,
+ "sigmaArcsec": 836.175,
+ "projectedAxisRatio": 0.9555874
+ },
+ {
+ "centralAmplitude": 8.387379,
+ "sigmaArcsec": 1258.9254,
+ "projectedAxisRatio": 1
+ }
+ ],
+ "evidence": {
+ "path": "labs/nebula/models/omega-centauri/physical-evidence.json",
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51"
+ },
+ "source": {
+ "url": "https://wwwmpa.mpa-garching.mpg.de/mpa/publications/preprints/pp2013/MPA3537.pdf",
+ "sha256": "5701eed16944eb96e0d3e8dea258f909e99a1fa4a5a0305581e878909e70db92",
+ "locator": "D\u2019Souza & Rix2013, Table1, manuscript page12 (PDF page12), eight projected MGE components; arcmin dispersions multiplied by60."
+ },
+ "interpretation": "Published oblate MGE conditioned integrated starlight, interpreted as relative display light. Inclination50 degrees and PA100 degrees follow the selected literature model; the near/far tilt sign and six-sigma cutoff are authored. Gaussian widths retain their published angular scale at adopted distance5426pc and the common observation center. This is not measured star-by-star depth, calibrated luminosity, an N-body simulation or a completed bake/registration acceptance.",
+ "envelope": {
+ "scalePixels": 8,
+ "fraction": 0.95,
+ "floor": 0,
+ "depthSamples": 512,
+ "depthTrim": 0.002,
+ "chromaHalfSaturationQuantile": 0.5,
+ "chromaSkyQuantile": 0.1,
+ "chromaCoverageTaper": 0.5
+ },
+ "residualMaximumComponents": 4096
+}
diff --git a/src/objects/omega-centauri/source/bake-inputs/references/03-observation-structures.json b/src/objects/omega-centauri/source/bake-inputs/references/03-observation-structures.json
new file mode 100644
index 0000000000..7e205539d1
--- /dev/null
+++ b/src/objects/omega-centauri/source/bake-inputs/references/03-observation-structures.json
@@ -0,0 +1,7 @@
+{
+ "schema": "cssearth-observation-structure-recipe@1",
+ "observationRecipe": "labs/nebula/models/omega-centauri/observations.json",
+ "observationCatalogue": ".local/nebula-lab/observations/omega-centauri/observations.json",
+ "workingWidth": 768,
+ "wavelets": { "scales": 6, "significanceSigma": 2, "compactMaxScale": 1, "elongatedAxisRatio": 2.5, "minRegionPixels": 5, "connectivity": 8 }
+}
diff --git a/src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json b/src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json
new file mode 100644
index 0000000000..b7cb1cb954
--- /dev/null
+++ b/src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json
@@ -0,0 +1,697 @@
+{
+ "schema": "cssearth-nebula-physical-evidence@1",
+ "subjectId": "omega-centauri",
+ "asOf": "2026-09-20",
+ "status": "Published photometric and dynamical constraints for a conditional oblate integrated-starlight model. The historical King approximation is rejected; runtime acceptance belongs to the delivered object evidence.",
+ "parameterClasses": {
+ "observed": "measured observable or metadata",
+ "published-model": "conditional literature inference, including a value we derived from measured inputs via a standard, non-object-specific mathematical relation",
+ "authored": "our transport, display, or approximation choice"
+ },
+ "coordinateRule": "Common tangent frame: x west, y north, z away from Earth; centered on the SIMBAD/publisher-agreed cluster center.",
+ "sources": [
+ {
+ "id": "baumgardt-hilker-2018",
+ "title": "A catalogue of masses, structural parameters, and velocity dispersion profiles of 112 Milky Way globular clusters",
+ "url": "https://doi.org/10.1093/mnras/sty1057",
+ "locator": "NGC 5139 row: core radius, half-mass radius, total mass, M/L, central velocity dispersion",
+ "access": "Cited from the task brief and cross-referenced against the author's public globular-cluster parameter tables (https://people.smp.uq.edu.au/HolgerBaumgardt/globular/); the paper PDF itself was not downloaded in this session, so its exact table values are not independently re-derived here."
+ },
+ {
+ "id": "baumgardt-2021-distances",
+ "title": "Baumgardt et al. (2021) globular cluster distances",
+ "url": "https://people.smp.uq.edu.au/HolgerBaumgardt/globular/",
+ "locator": "NGC 5139 distance entry",
+ "access": "Cited from the task brief; not independently re-fetched in this session."
+ },
+ {
+ "id": "simbad-ngc5139",
+ "title": "SIMBAD astronomical database, NGC 5139",
+ "url": "https://simbad.cds.unistra.fr/simbad/sim-id?Ident=NGC+5139",
+ "locator": "J2000 ICRS coordinates, reference 2010AJ....140.1830G",
+ "access": "Fetched live in this session; RA 13:26:47.28, Dec -47:28:46.1."
+ },
+ {
+ "id": "eso1119b-publisher",
+ "title": "ESO/INAF-VST OmegaCAM image eso1119b",
+ "url": "https://www.eso.org/public/images/eso1119b/",
+ "locator": "Field of view, pixel scale, pointing center, orientation",
+ "access": "Fetched live in this session; pointing center RA 13:26:47.27, Dec -47:28:46.34; 50.88 x 50.88 arcmin; north 0.0 degrees left of vertical; already acquired/hash-pinned per labs/nebula/sources/reference-images.json (id eso1119b)."
+ },
+ {
+ "id": "white-shawl-1987",
+ "title": "White & Shawl 1987, ellipticities of Galactic globular clusters",
+ "url": "https://ui.adsabs.harvard.edu/abs/1987ApJ...317..246W",
+ "locator": "Omega Centauri flattening measurement",
+ "access": "Cited from the task brief and corroborated by secondary literature search in this session; the paper PDF was not downloaded, and published ellipticity for Omega Cen varies with radius across the literature (roughly 0.08 in the core to ~0.17-0.19 in the outskirts)."
+ },
+ {
+ "id": "king-1962",
+ "title": "King 1962, The structure of star clusters I: an empirical density law",
+ "url": "https://ui.adsabs.harvard.edu/abs/1962AJ.....67..471K",
+ "locator": "Empirical projected surface-density formula used as the profile family",
+ "access": "Standard, widely reproduced closed-form formula; used from memory, not refetched."
+ },
+ {
+ "id": "wolf-2010",
+ "title": "Wolf et al. 2010, accurate masses for dispersion-supported galaxies",
+ "url": "https://doi.org/10.1111/j.1365-2966.2009.15708.x",
+ "locator": "Approximate relation between projected half-light radius and 3D half-mass radius for a broad family of density profiles",
+ "access": "Cited from general knowledge of the standard result, not refetched; used here only to fix one shape parameter, see evidence id projected-half-light-approx."
+ },
+ {
+ "id": "van-de-ven-2006-structure",
+ "title": "van de Ven, van den Bosch, Verolme & de Zeeuw (2006), The dynamical distance and intrinsic structure of the globular cluster omega Centauri",
+ "url": "https://doi.org/10.1051/0004-6361:20053061",
+ "locator": "A&A 445, 513-543; arXiv:astro-ph/0509228. Axisymmetric Schwarzschild orbit-superposition dynamical model combining line-of-sight velocities and internal proper motions.",
+ "access": "Full arXiv PDF downloaded and sections 2.3.3, 2.3.4 and 7.1 inspected on 2026-09-20; photometric major-axis PA100 degrees and coordinate convention confirmed.",
+ "download": {
+ "url": "https://arxiv.org/pdf/astro-ph/0509228",
+ "bytes": 1253405,
+ "sha256": "091115f8b04cf247f0d8ca04e7a6a5840fab8daa24f427168b6451fb37c13dd6"
+ }
+ },
+ {
+ "id": "reijns-2006-radial-velocities",
+ "title": "Reijns, Seitzer, Arnold, Freeman, Ingerson, van den Bosch, van de Ven & de Zeeuw (2006), Radial velocities in the globular cluster omega Centauri",
+ "url": "https://arxiv.org/abs/astro-ph/0509227",
+ "locator": "A&A 445, 503-511. 2756 radial velocities for 1966 stars (1589 members), ARGUS/CTIO 4m.",
+ "access": "Abstract fetched live in this session."
+ },
+ {
+ "id": "haberle-2024-omegacat2",
+ "title": "Haberle, Neumayer, Bellini, Libralato et al. (2024), oMEGACat II: Photometry and proper motions for 1.4 million stars in Omega Centauri and its rotation in the plane of the sky",
+ "url": "https://arxiv.org/html/2404.03722v2",
+ "locator": "Accepted v2, section 8.2: kinematic PA104 +/-1 degree and inclination43.9 +/-1.3 degrees.",
+ "access": "Full accepted-v2 HTML retrieved on 2026-09-20; section8.2 inspected.",
+ "download": {
+ "bytes": 740755,
+ "sha256": "9e46c2738830485c2f0eec0f7bde39654434a02a03a1d445010f2c37531b73c9"
+ }
+ },
+ {
+ "id": "bianchini-2013-flattening",
+ "title": "Bianchini, Varri, Bertin & Zocchi (2013), The flattening of globular clusters: internal rotation or velocity anisotropy?",
+ "url": "https://arxiv.org/abs/1302.0717",
+ "locator": "Axisymmetric rotating-anisotropic dynamical models applied to omega Centauri and 47 Tucanae.",
+ "access": "Abstract fetched live in this session."
+ },
+ {
+ "id": "pechetti-2024-muse-core",
+ "title": "Pechetti, Kamann, Krajnovic, Seth, van de Ven, Neumayer, Dreizler, Weilbacher, Martens & Wragg (2024), omega Centauri: a MUSE discovery of a counter-rotating core",
+ "url": "https://doi.org/10.1093/mnras/stae294",
+ "locator": "MNRAS 528, 4941-4957; arXiv:2401.15149.",
+ "access": "Abstract and title confirmed via live search in this session; not independently fetched in full."
+ },
+ {
+ "id": "trager-king-djorgovski-1995",
+ "title": "Trager, King & Djorgovski (1995), Catalogue of Galactic globular-cluster surface-brightness profiles",
+ "url": "https://ui.adsabs.harvard.edu/abs/1995AJ....109..218T",
+ "locator": "AJ 109, 218. Compiled surface-brightness profiles and King-model concentrations for 125 Milky Way globular clusters, including NGC 5139.",
+ "access": "Confirmed to exist and to cover this method via live search in this session; the specific NGC 5139 numeric table row (central surface brightness, concentration c, core radius) was not retrieved from the primary table itself."
+ },
+ {
+ "id": "mclaughlin-vandermarel-2005",
+ "title": "McLaughlin & van der Marel (2005), Resolved massive star clusters in the Milky Way and its satellites: brightness profiles and a catalogue of fundamental parameters",
+ "url": "https://arxiv.org/abs/astro-ph/0605132",
+ "locator": "CDS J/ApJS/161/304 Table10, NGC5139 rows229-231; Table11 derived structural radii.",
+ "access": "Complete CDS ReadMe, table10.dat and table11.dat downloaded on 2026-09-20. NGC5139 rows decoded using the archive byte-column definitions. Wilson and King(1966) fits use54 points; they are not the empirical King(1962) law.",
+ "tables": [
+ {
+ "url": "https://cdsarc.cds.unistra.fr/ftp/J/ApJS/161/304/ReadMe",
+ "bytes": 27936,
+ "sha256": "48b6e46ffeceb668e3cb108a1ffd8723df2382e0a07a6174260e199354e757b5"
+ },
+ {
+ "url": "https://cdsarc.cds.unistra.fr/ftp/J/ApJS/161/304/table10.dat",
+ "bytes": 75735,
+ "sha256": "536b829f310535759f572dd781f2c9d7cec602999d53f07c2bce9b0638f9abf1"
+ },
+ {
+ "url": "https://cdsarc.cds.unistra.fr/ftp/J/ApJS/161/304/table11.dat",
+ "bytes": 84456,
+ "sha256": "8707d3e19c864e058138bd7d0132ec06c97521a06ac4051132b3f7c149260a2d"
+ }
+ ]
+ },
+ {
+ "id": "noyola-gebhardt-bergmann-2008",
+ "title": "Noyola, Gebhardt & Bergmann (2008), Gemini and Hubble Space Telescope evidence for an intermediate-mass black hole in omega Centauri",
+ "url": "https://arxiv.org/abs/0801.2782",
+ "locator": "ApJ 676, 1008. HST ACS integrated-light surface-brightness profile of the cluster center.",
+ "access": "Confirmed via live search in this session; central cusp slope value taken from search summary of the paper, not from a direct fetch of the paper text."
+ },
+ {
+ "id": "baumgardt-vasiliev-2021-paper",
+ "title": "Baumgardt & Vasiliev (2021), Accurate distances to Galactic globular clusters through a combination of Gaia EDR3, HST, and literature data",
+ "url": "https://doi.org/10.1093/mnras/stab1474",
+ "locator": "Primary article distance table, NGC5139 row, combined-distance column:5.426 +0.047/-0.047 kpc.",
+ "access": "Primary article table inspected directly on 2026-09-20 at https://academic.oup.com/mnras/article/505/4/5957/6283731; exact distance and both uncertainty bounds confirmed."
+ },
+ {
+ "id": "merritt-1997",
+ "title": "Merritt, Meylan & Mayor (1997), The stellar dynamics of omega Centauri",
+ "url": "https://ui.adsabs.harvard.edu/abs/1997AJ....114.1074M",
+ "locator": "AJ 114, 1074. Early line-of-sight rotation-curve measurement.",
+ "access": "Confirmed via live search in this session; peak rotation value taken from search summary, not from a direct fetch of the paper text."
+ },
+ {
+ "id": "dsouza-rix-2013-mge",
+ "title": "D’Souza & Rix (2013), Mass estimates from stellar proper motions: the mass of omega Centauri",
+ "url": "https://doi.org/10.1093/mnras/sts426",
+ "locator": "Section4.3 and Table1, manuscript page12: eight Gaussian fit to Ferraro2006 number-density and Geyer1983 flattening profiles.",
+ "access": "Complete author-hosted PDF downloaded and table transcribed on2026-09-20; all eight rows cross-checked against Watkins2013 Table1. Coefficient error bars/covariance are not supplied in the table.",
+ "download": {
+ "url": "https://wwwmpa.mpa-garching.mpg.de/mpa/publications/preprints/pp2013/MPA3537.pdf",
+ "bytes": 480080,
+ "sha256": "5701eed16944eb96e0d3e8dea258f909e99a1fa4a5a0305581e878909e70db92"
+ }
+ },
+ {
+ "id": "watkins-2013-mge-reuse",
+ "title": "Watkins, van de Ven, den Brok & van den Bosch (2013), Discrete dynamical models of omega Centauri",
+ "url": "https://academic.oup.com/mnras/article/436/3/2598/1258888",
+ "locator": "Section4.1 and Table1; central brightness in solar luminosities/pc2, sigma along the major axis in arcmin, projected flattening.",
+ "access": "Primary article table inspected on2026-09-20. Exact same eight MGE rows; caption resolves amplitude semantics. PA-80 degrees equals100 degrees for the undirected major axis; their signed rotation coefficients are not used here."
+ }
+ ],
+ "evidence": [
+ {
+ "id": "distance",
+ "classification": "observed",
+ "sourceIds": [
+ "baumgardt-vasiliev-2021-paper"
+ ],
+ "quantity": "heliocentric distance",
+ "value": {
+ "distancePc": 5426,
+ "uncertaintyPc": 47
+ },
+ "unit": "pc",
+ "scope": "Whole cluster",
+ "limitations": "Adopted angular-to-physical scale from the combined Gaia EDR3, HST and literature distance. Earlier rounded5430 +/-50pc is retained in historical King evidence; changing distance does not refit the published MGE angular shape or establish a dynamical mass."
+ },
+ {
+ "id": "total-mass",
+ "classification": "published-model",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "total dynamical mass",
+ "value": {
+ "massSolar": 3940000.0
+ },
+ "unit": "solar masses",
+ "scope": "Whole cluster",
+ "limitations": "N-body/dynamical-model mass, not directly used by the light-emissivity profile in this recipe; recorded for completeness and for the mass-segregation limitation below."
+ },
+ {
+ "id": "half-mass-radius",
+ "classification": "published-model",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "3D half-mass radius",
+ "value": {
+ "radiusPc": 10.42
+ },
+ "unit": "pc",
+ "scope": "Whole cluster, dynamical model fit",
+ "limitations": "A 3D dynamical mass radius, not a projected light radius. The historical King-Abel conversion to a light radius is rejected for the selected MGE; mass segregation prevents treating the two as interchangeable."
+ },
+ {
+ "id": "core-radius",
+ "classification": "published-model",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "3D King core radius",
+ "value": {
+ "radiusPc": 4.54
+ },
+ "unit": "pc",
+ "scope": "Whole cluster, dynamical model fit",
+ "limitations": "Historical King-Abel experiment set this 3D dynamical core radius equal to a projected King radius. That substitution is rejected for the selected MGE recipe; this value is retained as historical context only."
+ },
+ {
+ "id": "mass-to-light",
+ "classification": "observed",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "V-band mass-to-light ratio",
+ "value": {
+ "mOverLvSolar": 2.8,
+ "uncertainty": 0.1
+ },
+ "unit": "Msun/Lsun (V)",
+ "scope": "Whole cluster",
+ "limitations": "Recorded for completeness; not used to scale the recipe's relative emissivity, which is unitless display light, not calibrated luminosity."
+ },
+ {
+ "id": "central-velocity-dispersion",
+ "classification": "observed",
+ "sourceIds": [
+ "baumgardt-hilker-2018"
+ ],
+ "quantity": "central velocity dispersion",
+ "value": {
+ "sigmaKmS": 18.1
+ },
+ "unit": "km/s",
+ "scope": "Cluster center",
+ "limitations": "Kinematic evidence, not used in this image/light recipe; recorded because it is part of the measured structural package and could support a future kinematic cross-check."
+ },
+ {
+ "id": "ellipticity",
+ "classification": "observed",
+ "sourceIds": [
+ "white-shawl-1987"
+ ],
+ "quantity": "projected flattening 1 - b/a",
+ "value": {
+ "ellipticityApprox": 0.17
+ },
+ "unit": "dimensionless",
+ "scope": "Whole cluster; varies with radius across the literature (roughly 0.08 core to ~0.19 outskirts)",
+ "limitations": "Historical summary, not a constant flattening consumed by the selected recipe. The MGE uses all eight published projected Gaussian axis ratios to represent radial variation; it does not assume spherical symmetry."
+ },
+ {
+ "id": "cluster-center-agreement",
+ "classification": "observed",
+ "sourceIds": [
+ "simbad-ngc5139",
+ "eso1119b-publisher"
+ ],
+ "quantity": "publisher pointing center vs. independent catalogue position",
+ "value": {
+ "simbadRaDec": [
+ "13:26:47.28",
+ "-47:28:46.1"
+ ],
+ "publisherRaDec": [
+ "13:26:47.27",
+ "-47:28:46.34"
+ ],
+ "separationArcsec": 0.24,
+ "nativePixelScaleArcsecPerPx": 0.21
+ },
+ "unit": "arcsec",
+ "scope": "Whole-image registration check",
+ "limitations": "The stated0.24arcsec separation is approximately1.14 native pixels at0.21arcsec/pixel, so it is a center sanity check, not sub-pixel evidence or an independent field-star registration. The MGE center is explicitly re-anchored to observations.json; native registration belongs to its separately measured transform."
+ },
+ {
+ "id": "king-profile-family",
+ "classification": "published-model",
+ "sourceIds": [
+ "king-1962"
+ ],
+ "quantity": "single-mass empirical projected surface-density law",
+ "value": {
+ "formula": "Sigma(R) proportional to [ (1+(R/rc)^2)^-1/2 - (1+(rt/rc)^2)^-1/2 ]^2 for R <= rt"
+ },
+ "unit": "n/a",
+ "scope": "Adopted profile family for the projected light distribution",
+ "limitations": "Historical King-Abel experiment only; not consumed by photometric-mge.json. Standard single-mass isotropic empirical law; Omega Centauri has multiple stellar populations and measurable rotation/flattening that a single-mass spherical King law does not capture.",
+ "selection": "rejected-historical"
+ },
+ {
+ "id": "projected-half-light-approx",
+ "classification": "published-model",
+ "sourceIds": [
+ "wolf-2010"
+ ],
+ "quantity": "projected half-light radius as a fraction of the 3D half-mass radius",
+ "value": {
+ "factor": 0.75
+ },
+ "unit": "dimensionless",
+ "scope": "Used once, to convert the measured 3D half-mass radius into a target projected half-light radius for solving the King concentration",
+ "limitations": "Historical King-Abel experiment only; not consumed by photometric-mge.json. This factor is an approximate, broadly-applicable result for centrally concentrated dispersion-supported systems, not a value measured for Omega Centauri specifically.",
+ "selection": "rejected-historical"
+ },
+ {
+ "id": "abel-inverted-emissivity",
+ "classification": "published-model",
+ "sourceIds": [
+ "king-1962",
+ "wolf-2010"
+ ],
+ "quantity": "spherically symmetric 3D relative emissivity vs. radius, derived by Abel-inverting the fitted King projected profile",
+ "value": {
+ "path": "labs/nebula/models/omega-centauri/king-abel-profile.json"
+ },
+ "unit": "relative (peak-normalized)",
+ "scope": "0 to the solved tidal radius (46.4 pc)",
+ "limitations": "Historical King-Abel experiment only; not consumed by photometric-mge.json. Deterministic consequence of assumed spherical symmetry plus the King profile family and its two fixed shape parameters (approximations A and B recorded in king-abel-profile.json). It is not a released, independently measured 3D point table; it is our own Abel-inversion computation from measured/published-model inputs. The single strongest physical limitation is sphericity itself: Omega Centauri's true ellipticity (~0.08-0.19, entry 'ellipticity') means the real 3D structure is oblate/triaxial, not spherical, and this profile cannot represent that flattening.",
+ "selection": "rejected-historical"
+ },
+ {
+ "id": "no-3d-baking-baseline",
+ "classification": "authored",
+ "sourceIds": [
+ "king-1962"
+ ],
+ "quantity": "historical implementation blocker for the original King-Abel experiment",
+ "value": {
+ "status": "historical-blocker"
+ },
+ "unit": "n/a",
+ "scope": "Lab tooling",
+ "limitations": "The original experiment had no external fixed-density simulation baseline. The selected analytic photometric MGE is a separate published-model prior; this historical entry does not establish that the new recipe has been baked, visually accepted or promoted."
+ },
+ {
+ "id": "inclination-van-de-ven-2006",
+ "classification": "published-model",
+ "sourceIds": [
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "inclination of the cluster's symmetry axis to the line of sight",
+ "value": {
+ "inclinationDeg": 50,
+ "uncertaintyDeg": 4,
+ "meanIntrinsicAxialRatio": 0.78,
+ "axialRatioUncertainty": 0.03
+ },
+ "unit": "degrees",
+ "scope": "Whole cluster; axisymmetric Schwarzschild orbit-superposition model fit to line-of-sight velocities plus internal proper motions",
+ "limitations": "Axisymmetric dynamical inference, not direct geometry. The source reports50 +/-4 degrees and dynamical distance4.8 +/-0.3kpc. D’Souza2013 subsequently adopts50 +/-3 degrees for its MGE analysis; that is the selected model convention, not an independently combined uncertainty. The recipe uses the newer observed distance only to set physical scale."
+ },
+ {
+ "id": "inclination-haberle-2024",
+ "classification": "published-model",
+ "sourceIds": [
+ "haberle-2024-omegacat2"
+ ],
+ "quantity": "inclination, independent modern check",
+ "value": {
+ "inclinationDeg": 43.9,
+ "uncertaintyDeg": 1.3
+ },
+ "unit": "degrees",
+ "scope": "Inner few arcminutes, from combined HST+MUSE proper motions (plane-of-sky rotation)",
+ "limitations": "Accepted-v2 axisymmetric kinematic inference, not direct depth. The1.3-degree interval crosses the selected MGE admissibility boundary43.603810 degrees; this newer fit cannot be substituted as an unconstrained inclination distribution into the older eight-Gaussian profile. Retained as a competing result, not erased or averaged with the selected50-degree convention."
+ },
+ {
+ "id": "rotation-amplitude-reijns-2006",
+ "classification": "observed",
+ "sourceIds": [
+ "reijns-2006-radial-velocities"
+ ],
+ "quantity": "line-of-sight rotation amplitude",
+ "value": {
+ "amplitudeKmS": 6,
+ "radiusRangeArcmin": [
+ 6,
+ 10
+ ]
+ },
+ "unit": "km/s",
+ "scope": "Radial-velocity survey, 1589 member stars",
+ "limitations": "Directly observed rotation signal (not a model output), but only along the line of sight; does not by itself give a 3D rotation axis or amplitude. Velocity dispersion drops from about 15 km/s in the inner few arcmin to about 6 km/s at 25 arcmin, so the rotation amplitude is a sizeable fraction of the local dispersion in the outer cluster."
+ },
+ {
+ "id": "rotation-amplitude-merritt-1997",
+ "classification": "observed",
+ "sourceIds": [
+ "merritt-1997"
+ ],
+ "quantity": "earlier line-of-sight rotation amplitude estimate",
+ "value": {
+ "amplitudeKmS": 7.9,
+ "radiusPc": 11
+ },
+ "unit": "km/s",
+ "scope": "Earlier radial-velocity dataset, independent of Reijns et al. (2006)",
+ "limitations": "Recorded from a search-engine summary of the paper, not a direct fetch of the paper text in this session, so treat the exact 7.9 km/s figure as approximate pending primary verification. Given alongside rotation-amplitude-reijns-2006 to show two independent studies agree the amplitude is of order 6-8 km/s, not to imply a precise consensus number."
+ },
+ {
+ "id": "rotation-flattening-relation",
+ "classification": "published-model",
+ "sourceIds": [
+ "bianchini-2013-flattening"
+ ],
+ "quantity": "whether rotation alone explains the photometric flattening",
+ "value": {
+ "conclusion": "not fully; a purely rotationally-flattened oblate isotropic model under-/over-predicts the observed ellipticity profile at some radii, and matching it needs added radial velocity anisotropy"
+ },
+ "unit": "n/a",
+ "scope": "Whole cluster, axisymmetric rotating-anisotropic dynamical models",
+ "limitations": "This is model-dependent evidence, not a raw measurement. Its direct consequence for this ledger: an oblate model built only from a measured ellipticity, position angle and inclination (isotropic King/Wilson made oblate) is known to be an approximation even under the axisymmetric assumption; velocity anisotropy, not just rotation, contributes to the real shape."
+ },
+ {
+ "id": "counter-rotating-core",
+ "classification": "observed",
+ "sourceIds": [
+ "pechetti-2024-muse-core"
+ ],
+ "quantity": "kinematically distinct core",
+ "value": {
+ "counterRotatesInner": true,
+ "innerRadiusArcsec": 20,
+ "innerRadiusPc": 0.5
+ },
+ "unit": "n/a",
+ "scope": "Innermost ~20 arcsec (~0.5 pc), MUSE narrow+wide field integral-field spectroscopy",
+ "limitations": "Direct observational evidence that the cluster's inner few tenths of a parsec rotate opposite to the bulk cluster rotation. This is incompatible with a single rigid-body oblate rotation pattern extended all the way to the center; any oblate King/Wilson model built from bulk ellipticity/PA/inclination necessarily misses this innermost feature, which occupies a negligible fraction of the cluster's volume but is a real, measured departure from simple oblate rotation."
+ },
+ {
+ "id": "kinematic-disk-component",
+ "classification": "published-model",
+ "sourceIds": [
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "separate disk-like kinematic substructure",
+ "value": {
+ "radialRangeArcmin": [
+ 1,
+ 3
+ ],
+ "massFraction": 0.04
+ },
+ "unit": "dimensionless (mass fraction)",
+ "scope": "Axisymmetric dynamical model, intermediate radii",
+ "limitations": "A model-inferred substructure (about 4% of total mass) in the same dynamical fit that gives the headline inclination number above; recorded because it is additional evidence that the cluster's true structure is not a single smooth oblate body, even though it is a small mass fraction and localized to a specific radial range."
+ },
+ {
+ "id": "king-wilson-fit-comparison",
+ "classification": "published-model",
+ "sourceIds": [
+ "mclaughlin-vandermarel-2005"
+ ],
+ "quantity": "comparison of published King(1966), Wilson(1975) and power-law fits",
+ "value": {
+ "pointCount": 54,
+ "unreducedChiSquared": {
+ "king1966": 89.81,
+ "wilson1975": 24.28,
+ "powerLaw": 76.25
+ },
+ "wilson": {
+ "centralPotentialW0": 4.7,
+ "centralPotentialUncertainty": 0.1,
+ "scaleRadiusArcsec": 196.81,
+ "scaleRadiusUncertaintyPlusArcsec": 7.08,
+ "scaleRadiusUncertaintyMinusArcsec": 7.01,
+ "concentrationLogRtOverR0": 1.34,
+ "concentrationUncertainty": 0.03,
+ "centralSurfaceBrightnessMagArcsec2": 16.52,
+ "centralSurfaceBrightnessUncertainty": 0.05,
+ "paperDistancePc": 5300
+ }
+ },
+ "unit": "n/a",
+ "scope": "Whole-cluster projected surface-brightness photometry",
+ "limitations": "Actual CDS rows now retrieved. Wilson is a better fit within this comparison, not proof that every King-family or other profile is impossible. r0 is a model scale radius, not the projected half-power core radius. Wilson requires its own distribution-function/Poisson solution; do not insert these parameters into King1962. The selected MGE already supplies a published radially varying flattening and an analytic deprojection."
+ },
+ {
+ "id": "central-cusp-noyola-2008",
+ "classification": "observed",
+ "sourceIds": [
+ "noyola-gebhardt-bergmann-2008"
+ ],
+ "quantity": "central surface-brightness slope",
+ "value": {
+ "logarithmicSlope": -0.08
+ },
+ "unit": "dimensionless (d log I / d log r)",
+ "scope": "Innermost several arcsec, HST ACS integrated light",
+ "limitations": "Shows a shallow rising cusp rather than a flat King core at the very center; part of the still-debated intermediate-mass-black-hole literature for this cluster (not adjudicated here), and irrelevant at the parsec scales the bulk King-Abel profile in this ledger targets, but recorded because it shows the innermost profile shape is not simply flat."
+ },
+ {
+ "id": "distance-confirmation-baumgardt-vasiliev-2021",
+ "classification": "observed",
+ "sourceIds": [
+ "baumgardt-vasiliev-2021-paper"
+ ],
+ "quantity": "adopted heliocentric distance from combined observations",
+ "value": {
+ "distancePc": 5426,
+ "uncertaintyPc": 47
+ },
+ "unit": "pc",
+ "scope": "Whole cluster, Gaia EDR3 + HST + literature combination",
+ "limitations": "Primary table now inspected directly. This replaces the earlier rounded5430 +/-50pc input for the MGE recipe, while preserving the original King-Abel artifact unchanged."
+ },
+ {
+ "id": "position-angle-not-sourced",
+ "classification": "authored",
+ "sourceIds": [
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "status note: position angle of the major axis on the sky",
+ "value": {
+ "status": "historical-gap-resolved",
+ "resolvedBy": "photometric-major-axis-position-angle"
+ },
+ "unit": "n/a",
+ "scope": "Whole cluster",
+ "limitations": "Initial search summaries did not establish PA. Full primary section2.3.3 now supplies photometric major-axis100 degrees; the separate104 +/-1 degree modern kinematic fit remains recorded. This preserved entry is not a current missing-source claim."
+ },
+ {
+ "id": "triaxiality-not-sourced",
+ "classification": "authored",
+ "sourceIds": [],
+ "quantity": "status note: dedicated triaxial (three-axis) shape measurement",
+ "value": {
+ "status": "not found in this session"
+ },
+ "unit": "n/a",
+ "scope": "Whole cluster",
+ "limitations": "No direct triaxial spatial fit was qualified for this recipe. Counter-rotation, kinematic substructure and differing fitted inclinations do not by themselves prove triaxiality. The oblate MGE is an explicit conditional approximation; it does not exclude more complex structure."
+ },
+ {
+ "id": "photometric-mge-fit",
+ "classification": "published-model",
+ "sourceIds": [
+ "dsouza-rix-2013-mge",
+ "watkins-2013-mge-reuse"
+ ],
+ "quantity": "eight-component projected Multi-Gaussian Expansion",
+ "value": {
+ "recipePath": "labs/nebula/models/omega-centauri/photometric-mge.json",
+ "gaussianCount": 8,
+ "sourceSigmaUnit": "arcmin",
+ "recipeSigmaUnit": "arcsec",
+ "sigmaConversionFactor": 60,
+ "paperDistancePc": 5500,
+ "paperDistanceUncertaintyPc": 200,
+ "coefficientUncertainty": null
+ },
+ "unit": "central amplitude:Lsun/pc2; angular major-axis sigma:arcmin; projected axis ratio:dimensionless",
+ "scope": "Smooth projected stellar number-density/light and radial flattening profiles fitted in the cited paper; not individual star locations.",
+ "limitations": "Central projected amplitudes are transcribed as published, with no additional1/q correction. Use only relative amplitudes for conditional integrated image light. No coefficient uncertainties or covariance are supplied, and no errors are fabricated. Neither an absolute emissivity calibration nor a modern dynamical refit is performed."
+ },
+ {
+ "id": "photometric-major-axis-position-angle",
+ "classification": "observed",
+ "sourceIds": [
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "photometric major-axis position angle east of north",
+ "value": {
+ "positionAngleDegrees": 100,
+ "uncertaintyDegrees": null,
+ "radialRangeArcmin": [
+ 5,
+ 15
+ ]
+ },
+ "unit": "degrees",
+ "scope": "Elliptical isophotes fitted to the smoothed DSS image at5-15arcmin.",
+ "limitations": "The cited section does not give a formal PA uncertainty. This is an undirected major axis modulo180 degrees, not a signed rotation axis or evidence that all radii have identical PA."
+ },
+ {
+ "id": "mge-adopted-inclination",
+ "classification": "published-model",
+ "sourceIds": [
+ "dsouza-rix-2013-mge",
+ "van-de-ven-2006-structure"
+ ],
+ "quantity": "adopted inclination for the published oblate MGE interpretation",
+ "value": {
+ "inclinationDegrees": 50,
+ "paperAdoptedUncertaintyDegrees": 3,
+ "inclinationDefinition": "zero face-on,90 edge-on",
+ "minimumAdmissibleInclinationDegrees": 43.60381018068668
+ },
+ "unit": "degrees",
+ "scope": "Selected smooth axisymmetric deprojection.",
+ "limitations": "D’Souza2013 adopts50 +/-3 degrees citing van de Ven2006, whose own reported interval is50 +/-4 degrees. These are model-dependent conventions, not a new precision estimate. The recipe uses the central50-degree value; all components require qProjected squared greater than cos(inclination) squared."
+ },
+ {
+ "id": "modern-kinematic-position-angle",
+ "classification": "published-model",
+ "sourceIds": [
+ "haberle-2024-omegacat2"
+ ],
+ "quantity": "modern fitted kinematic position angle",
+ "value": {
+ "positionAngleDegrees": 104,
+ "uncertaintyDegrees": 1
+ },
+ "unit": "degrees east of north",
+ "scope": "HST+MUSE rotation fit in accepted-v2 section8.2.",
+ "limitations": "The quoted1-degree uncertainty follows the fitting step size. A kinematic fit is not interchangeable with the older photometric-isophote orientation used by this recipe."
+ },
+ {
+ "id": "mge-observation-frame-center",
+ "classification": "authored",
+ "sourceIds": [
+ "eso1119b-publisher",
+ "dsouza-rix-2013-mge"
+ ],
+ "quantity": "adopted common tangent-frame center",
+ "value": {
+ "centerIcrsDegrees": [
+ 201.696958,
+ -47.479539
+ ],
+ "observationRecipe": "labs/nebula/models/omega-centauri/observations.json",
+ "paperKinematicCenterIcrsDegrees": [
+ 201.69065,
+ -47.47855
+ ]
+ },
+ "unit": "degrees ICRS",
+ "scope": "One common center for the two image lenses and the conditional spatial prior.",
+ "limitations": "The published radial shape is explicitly re-centered on the observation-frame center; it is not an assertion that the older paper used this same center. Center metadata does not replace independent image registration."
+ },
+ {
+ "id": "mge-oblate-deprojection",
+ "classification": "published-model",
+ "sourceIds": [
+ "dsouza-rix-2013-mge",
+ "watkins-2013-mge-reuse"
+ ],
+ "quantity": "analytic oblate deprojection of the published projected Gaussians",
+ "value": {
+ "methodPath": "labs/nebula/models/omega-centauri/mge-method.md",
+ "intrinsicAxisRatio": "sqrt((qProjected^2-cos(i)^2)/sin(i)^2)",
+ "centralRelativeDensity": "Sigma0*qProjected/(sqrt(2*pi)*sigma*qIntrinsic)",
+ "density": "sum rho0*exp(-(R^2+zIntrinsic^2/qIntrinsic^2)/(2*sigma^2))",
+ "frame": "x west,y north,z away from Earth"
+ },
+ "unit": "relative light per angular-depth unit in the sampler",
+ "scope": "Conditional continuous depth prior for finite integrated-light emitters.",
+ "limitations": "An analytic inference under axisymmetry, not a released XYZ catalogue or measured star-by-star depth. It does not recover population-specific mass segregation, central counter-rotation or triaxial structure. Angular widths stay fixed when physical distance is changed."
+ },
+ {
+ "id": "mge-authored-transport",
+ "classification": "authored",
+ "sourceIds": [
+ "dsouza-rix-2013-mge"
+ ],
+ "quantity": "finite-support and mirror choices for conditional image-light transport",
+ "value": {
+ "lineOfSightTiltSign": 1,
+ "cutoffSigma": 6,
+ "amplitudeNormalization": "divide all projected central amplitudes by their sum",
+ "stellarTreatment": "preserve integrated starlight"
+ },
+ "unit": "dimensionless",
+ "scope": "Offline conditional depth/material construction; common geometry for every lens.",
+ "limitations": "The projected image cannot choose the near/far tilt; sign+1 is authored. Six-sigma truncation is a numerical support bound, not a measured tidal edge. The published profile conditions finite3D image emitters; it does not identify individual member stars or justify copying the same image through every depth. Source registration, actual bake, side-view acceptance and app promotion require their own evidence."
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/source/bake-inputs/references/05-observations.json b/src/objects/omega-centauri/source/bake-inputs/references/05-observations.json
new file mode 100644
index 0000000000..9edd20d4f7
--- /dev/null
+++ b/src/objects/omega-centauri/source/bake-inputs/references/05-observations.json
@@ -0,0 +1,79 @@
+{
+ "schema": "cssearth-nebula-observation-recipe@1",
+ "id": "omega-centauri",
+ "referenceId": "eso1119b",
+ "frame": {
+ "width": 1024,
+ "height": 1024,
+ "fieldArcminutes": [55, 55],
+ "centerIcrsDegrees": [201.696958, -47.479539],
+ "northUp": true
+ },
+ "nativeRemoval": {
+ "model": {
+ "path": ".local/open-star-removal/noxGeneratorColor.pb",
+ "sha256": "d54bdca728d1d6db0b3eef41d4187d327909d1ec5cd2a71485bfa9d7924ba546"
+ },
+ "scriptSha256": "5db7c54230ba2032abb361cc2d59ef0e2e42114f27ef2036474c877058a6b752"
+ },
+ "images": [
+ {
+ "id": "eso1119b",
+ "label": "ESO VST/OmegaCAM · optical G/R/I",
+ "localCache": ".local/nebula-lab/source-originals/eso1119b.tif",
+ "url": "https://cdn.eso.org/images/original/eso1119b.tif",
+ "page": "https://www.eso.org/public/images/eso1119b/",
+ "sha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "width": 14540,
+ "height": 14540,
+ "fieldArcminutes": [50.88, 50.88],
+ "centerIcrsDegrees": [201.69695833333333, -47.47953888888889],
+ "northRightDegrees": -0.00051173061,
+ "bands": "Optical G (480nm) / R (625nm) / I (770nm)",
+ "credit": "ESO/INAF-VST/OmegaCAM. Acknowledgement: A. Grado, L. Limatola/INAF-Capodimonte Observatory",
+ "termsUrl": "https://www.eso.org/public/outreach/copyright/",
+ "registrationMode": "field-stars",
+ "registrationDetection": { "sourceMaximum": 8000, "referenceMaximum": 8000, "maximumStars": 12000 },
+ "stellarTreatment": "preserve",
+ "wcs": {
+ "projection": "TAN",
+ "coordinateFrame": "ICRS",
+ "referenceDimension": [1280, 1280],
+ "referencePixel": [633.141898442, 623.135269606],
+ "referenceValueDeg": [201.696944, -47.479539],
+ "scaleDeg": [-0.00066245096, 0.00066245096],
+ "rotationDeg": 0.00051173061
+ },
+ "metadataNote": "Exact embedded AVM WCS from the hash-pinned native TIFF, including its 1280-pixel reference grid and noncentral reference pixel. The AVM sky reference is not the raster centre. Native 14540x14540; the ignored derivative is an 8192px WebP. Relative alignment is tested against WFI; absolute astrometry remains publisher metadata."
+ },
+ {
+ "id": "eso0844a",
+ "label": "ESO 2.2m MPG/WFI · optical B/V/I",
+ "localCache": ".local/nebula-lab/source-originals/eso0844a.tif",
+ "url": "https://cdn.eso.org/images/original/eso0844a.tif",
+ "page": "https://www.eso.org/public/images/eso0844a/",
+ "sha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429",
+ "width": 8040,
+ "height": 7560,
+ "fieldArcminutes": [31.88, 29.99],
+ "centerIcrsDegrees": [201.69716666666667, -47.479683333333334],
+ "northRightDegrees": 0.00724264734,
+ "bands": "Optical B (451nm) / V (539nm) / I (783nm)",
+ "credit": "ESO",
+ "termsUrl": "https://www.eso.org/public/outreach/copyright/",
+ "registrationMode": "field-stars",
+ "registrationDetection": { "sourceMaximum": 8000, "referenceMaximum": 8000, "maximumStars": 12000 },
+ "stellarTreatment": "preserve",
+ "wcs": {
+ "projection": "TAN",
+ "coordinateFrame": "ICRS",
+ "referenceDimension": [1280, 1204],
+ "referencePixel": [620.447024342, 636.048276073],
+ "referenceValueDeg": [201.697148, -47.479684],
+ "scaleDeg": [-0.00041507559, 0.00041507559],
+ "rotationDeg": -0.00724264734
+ },
+ "metadataNote": "Exact embedded AVM WCS from the hash-pinned native TIFF, including its 1280x1204 reference grid and noncentral reference pixel. Independent telescope/instrument/epoch from VST/OmegaCAM. Publisher notes Digitized Sky Survey data fills some gaps in this mosaic; DSS-filled regions are not independently attributed star detections."
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/source/delivery.json b/src/objects/omega-centauri/source/delivery.json
new file mode 100644
index 0000000000..f12f136eef
--- /dev/null
+++ b/src/objects/omega-centauri/source/delivery.json
@@ -0,0 +1,56 @@
+{
+ "schema": "cssearth-nebula-delivery@1",
+ "id": "omega-centauri",
+ "method": "compiler",
+ "request": {
+ "path": "src/objects/omega-centauri/source/request.json",
+ "sha256": "043c6218aedd90269ec68822cfa2d729d731b7995150c0e01af8b5c68127e241"
+ },
+ "inputPins": [
+ {
+ "path": "labs/nebula/models/omega-centauri/compiler.json",
+ "sha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/photometric-mge.json",
+ "sha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/observation-structures.json",
+ "sha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/physical-evidence.json",
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51"
+ },
+ {
+ "path": "labs/nebula/models/omega-centauri/observations.json",
+ "sha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2"
+ }
+ ],
+ "sky": {
+ "centerIcrsDegrees": [
+ 201.696958,
+ -47.479539
+ ],
+ "distancePc": 5426,
+ "imageRotationDegrees": 0,
+ "arcsecPerUnit": 1
+ },
+ "sourceUrl": "https://www.eso.org/public/images/eso1119b/",
+ "description": "Integrated cluster starlight on finite 3D emission components conditioned by a published oblate Gaussian profile. Inclination, near/far tilt and individual feature depths are model assumptions. Both optical lenses share geometry; no duplicate resolved-star layer is added. Colors are relative display emission, not calibrated luminosity or a measured stellar mass map.",
+ "defaultLens": "eso1119b",
+ "framingRadiusUnits": 1650,
+ "acceptedLabResult": "59ae6e1a336d5a7b0d946197decfbbc093beb39f0181fac2ef88300314c08b8e",
+ "compactMethod": "compiler",
+ "compactInputs": {
+ "path": "src/objects/omega-centauri/source/bake-inputs.json.gz",
+ "sha256": "c9f2535b13f4b4745179ec53b63240df1207ef7f38a4ea34dff76cba50a74d68"
+ },
+ "qualification": {
+ "status": "app-integration-candidate",
+ "visualHandoff": "failed",
+ "evidence": "src/objects/omega-centauri/evidence/2026-09-21/element-budget/handoffs.json",
+ "meaning": "Selected quota rebake of the unchanged fitted field; acceptedLabResult is an identity, not a passed visual acceptance receipt."
+ }
+}
diff --git a/src/objects/omega-centauri/source/manifest.json b/src/objects/omega-centauri/source/manifest.json
new file mode 100644
index 0000000000..37e893dcb8
--- /dev/null
+++ b/src/objects/omega-centauri/source/manifest.json
@@ -0,0 +1,249 @@
+{
+ "schema": "cssearth-volume-source-manifest@1",
+ "pathBase": "repository",
+ "inputs": [
+ {
+ "id": "eso1119b",
+ "path": ".local/nebula-lab/observations/omega-centauri/sources/eso1119b.tif",
+ "origin": "https://www.eso.org/public/images/eso1119b/",
+ "sourceUrl": "https://www.eso.org/public/images/eso1119b/",
+ "title": "Omega Centauri · VST/OmegaCAM · optical",
+ "credit": "ESO/INAF-VST/OmegaCAM. Acknowledgement: A. Grado, L. Limatola/INAF-Capodimonte Observatory",
+ "displayCredit": "ESO/INAF-VST/OmegaCAM. Acknowledgement: A. Grado, L. Limatola/INAF-Capodimonte Observatory",
+ "acquisition": "Unchanged native publisher TIFF restored through the pinned observation recipe. Integrated starlight is preserved. Embedded AVM includes the original reference grid and noncentral reference pixel; absolute astrometry remains publisher metadata.",
+ "expectedSha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "expectedBytes": 555370438,
+ "sourceBinding": {
+ "kind": "catalogued",
+ "references": [
+ {
+ "catalogueId": "eso-eso1119b",
+ "role": "material",
+ "evidence": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json#/images/0"
+ }
+ ]
+ },
+ "dependencies": [],
+ "lensId": "eso1119b",
+ "license": "ESO image-use terms: https://www.eso.org/public/outreach/copyright/; retain the complete credit.",
+ "capture": {
+ "attributions": [
+ {
+ "kind": "facility",
+ "facilityId": "vst",
+ "evidence": "https://www.eso.org/public/images/eso1119b/"
+ }
+ ],
+ "observation": {
+ "id": "eso1119b",
+ "target": "Omega Centauri",
+ "observedAt": null,
+ "instrument": "OmegaCAM",
+ "bands": "Optical G (480nm) / R (625nm) / I (770nm)",
+ "evidence": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json#/images/0; capture date is not inferred from the publication date."
+ }
+ }
+ },
+ {
+ "id": "eso0844a",
+ "path": ".local/nebula-lab/observations/omega-centauri/sources/eso0844a.tif",
+ "origin": "https://www.eso.org/public/images/eso0844a/",
+ "sourceUrl": "https://www.eso.org/public/images/eso0844a/",
+ "title": "Omega Centauri · MPG/ESO 2.2-m WFI · optical",
+ "credit": "ESO",
+ "displayCredit": "ESO",
+ "acquisition": "Unchanged native publisher TIFF restored through the pinned observation recipe. Integrated starlight is preserved. Embedded AVM includes the original reference grid and noncentral reference pixel; absolute astrometry remains publisher metadata.",
+ "expectedSha256": "384d1cfc31e6a2b78e5c7018f9eaecfe49a1014630bcb1bd8dceb563d69e6429",
+ "expectedBytes": 147797270,
+ "sourceBinding": {
+ "kind": "catalogued",
+ "references": [
+ {
+ "catalogueId": "eso-eso0844a",
+ "role": "material",
+ "evidence": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json#/images/1"
+ }
+ ]
+ },
+ "dependencies": [],
+ "lensId": "eso0844a",
+ "license": "ESO image-use terms: https://www.eso.org/public/outreach/copyright/; retain the complete credit.",
+ "capture": {
+ "attributions": [
+ {
+ "kind": "facility",
+ "facilityId": "mpg-eso-2-2m",
+ "evidence": "https://www.eso.org/public/images/eso0844a/"
+ }
+ ],
+ "observation": {
+ "id": "eso0844a",
+ "target": "Omega Centauri",
+ "observedAt": null,
+ "instrument": "Wide Field Imager (WFI)",
+ "bands": "Optical B (451nm) / V (539nm) / I (783nm)",
+ "evidence": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json#/images/1; capture date is not inferred from the publication date."
+ }
+ }
+ },
+ {
+ "id": "physical-evidence",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json",
+ "origin": "https://doi.org/10.1093/mnras/sts426",
+ "sourceUrl": "https://doi.org/10.1093/mnras/sts426",
+ "title": "Omega Centauri source evidence and model limits",
+ "credit": "D’Souza & Rix (2013); source interpretation by cssEarth",
+ "acquisition": "Byte-identical application copy of the physical-evidence ledger. The hash identifies this curated record; individual publications retain their own download identities and access qualifications.",
+ "expectedSha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51",
+ "expectedBytes": 36711,
+ "sourceBinding": {
+ "kind": "catalogued",
+ "references": [
+ {
+ "catalogueId": "dsouza-rix-2013",
+ "role": "method",
+ "evidence": "src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json"
+ }
+ ]
+ },
+ "dependencies": []
+ },
+ {
+ "id": "photometric-mge",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json",
+ "origin": "https://doi.org/10.1093/mnras/sts426",
+ "sourceUrl": "https://doi.org/10.1093/mnras/sts426",
+ "title": "Omega Centauri published MGE and explicit transport choices",
+ "credit": "D’Souza & Rix (2013); source interpretation by cssEarth",
+ "acquisition": "Byte-identical recipe copy. Published angular widths are converted from arcmin to arcsec; the recipe pins the complete source PDF and evidence ledger. Near/far sign and six-sigma cutoff are authored.",
+ "expectedSha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914",
+ "expectedBytes": 2508,
+ "sourceBinding": {
+ "kind": "catalogued",
+ "references": [
+ {
+ "catalogueId": "dsouza-rix-2013",
+ "role": "method",
+ "evidence": "src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json"
+ }
+ ]
+ },
+ "dependencies": []
+ },
+ {
+ "id": "distance",
+ "path": "src/objects/omega-centauri/source/nebula.json",
+ "origin": "https://doi.org/10.1093/mnras/stab1474",
+ "sourceUrl": "https://doi.org/10.1093/mnras/stab1474",
+ "title": "Omega Centauri adopted cluster distance",
+ "credit": "Baumgardt & Vasiliev (2021); source interpretation by cssEarth",
+ "acquisition": "Source-owned catalogue record preserves the exact 5426 +/-47 pc value and full-paper download pin. This file hash identifies the catalogue record, not the remote paper.",
+ "expectedSha256": "94257a65f14b1fb1fb74d6f893010928afead94b47cc3c4342399f14d332a991",
+ "expectedBytes": 2633,
+ "sourceBinding": {
+ "kind": "catalogued",
+ "references": [
+ {
+ "catalogueId": "baumgardt-vasiliev-2021",
+ "role": "method",
+ "evidence": "src/objects/omega-centauri/source/nebula.json"
+ }
+ ]
+ },
+ "dependencies": []
+ }
+ ],
+ "documents": [
+ {
+ "id": "recipe-compiler",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/01-compiler.json",
+ "expectedSha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f",
+ "expectedBytes": 1361,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Byte-identical source-owned scientific processing recipe; external input identities are retained in its content and the selected input records."
+ }
+ },
+ {
+ "id": "recipe-observation-structures",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/03-observation-structures.json",
+ "expectedSha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e",
+ "expectedBytes": 397,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Byte-identical source-owned scientific processing recipe; external input identities are retained in its content and the selected input records."
+ }
+ },
+ {
+ "id": "recipe-observations",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json",
+ "expectedSha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2",
+ "expectedBytes": 3842,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Byte-identical source-owned scientific processing recipe; external input identities are retained in its content and the selected input records."
+ }
+ },
+ {
+ "id": "compact-inputs",
+ "path": "src/objects/omega-centauri/source/bake-inputs.json.gz",
+ "expectedSha256": "c9f2535b13f4b4745179ec53b63240df1207ef7f38a4ea34dff76cba50a74d68",
+ "expectedBytes": 2229420,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Pinned pre-slice emission field, per-component materials and adaptive sampling; no derived runtime textures. Quantitative handoff qualification remains failed."
+ }
+ },
+ {
+ "id": "provenance-references",
+ "path": "src/objects/omega-centauri/source/provenance-references.json",
+ "expectedSha256": "9a749e5f63493aa373c47d09b1bfec5bd312c32b6896aafd0d51cb7a29e1d5d4",
+ "expectedBytes": 1856,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Maps source-owned evidence bytes to their exact published research revision."
+ }
+ },
+ {
+ "id": "replay-references",
+ "path": "src/objects/omega-centauri/source/replay-references.json",
+ "expectedSha256": "d289333b8061b55f07f97bad5bef7a515123cc2e5bf845dec0a50724ba6df358",
+ "expectedBytes": 1302,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Maps retained research input identities to the application-owned copies."
+ }
+ },
+ {
+ "id": "document-request",
+ "path": "src/objects/omega-centauri/source/request.json",
+ "expectedSha256": "043c6218aedd90269ec68822cfa2d729d731b7995150c0e01af8b5c68127e241",
+ "expectedBytes": 365,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Object-owned application candidate recipe or presentation; qualification and published source identities are recorded explicitly."
+ }
+ },
+ {
+ "id": "document-delivery",
+ "path": "src/objects/omega-centauri/source/delivery.json",
+ "expectedSha256": "d1e34564e98eae6ebe9d8d59b8cfd4ebe19c20e0c14f8279c72444f96faf36a5",
+ "expectedBytes": 2428,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Object-owned application candidate recipe or presentation; qualification and published source identities are recorded explicitly."
+ }
+ },
+ {
+ "id": "document-presentation",
+ "path": "src/objects/omega-centauri/source/presentation.json",
+ "expectedSha256": "3096d25d13618cb24f18e731c8d8980ed424b9acb13792ff1439d090ccea85f8",
+ "expectedBytes": 6277,
+ "sourceBinding": {
+ "kind": "local",
+ "reason": "Object-owned application candidate recipe or presentation; qualification and published source identities are recorded explicitly."
+ }
+ }
+ ],
+ "generatedIntermediates": []
+}
diff --git a/src/objects/omega-centauri/source/nebula.json b/src/objects/omega-centauri/source/nebula.json
new file mode 100644
index 0000000000..feef7071c4
--- /dev/null
+++ b/src/objects/omega-centauri/source/nebula.json
@@ -0,0 +1,66 @@
+{
+ "schema": "cssearth-nebula-catalog@1",
+ "frame": {
+ "referenceFrame": "sun-icrf",
+ "epochJdTt": 2461286.5
+ },
+ "sources": [
+ {
+ "id": "baumgardt-vasiliev-2021",
+ "url": "https://arxiv.org/pdf/2105.09526",
+ "sha256": "2272702d483b16695ee19346a207e3a69bc75a27e585b3390af79ffd62254e24",
+ "bytes": 4020280,
+ "citation": "Baumgardt & Vasiliev (2021), Accurate distances to Galactic globular clusters through a combination of Gaia EDR3, HST and literature data, Table 2, NGC 5139: combined distance 5.426 +/- 0.047 kpc. Full downloaded paper bytes."
+ },
+ {
+ "id": "eso1119b-frame",
+ "url": "https://cdn.eso.org/images/original/eso1119b.tif",
+ "sha256": "8df2e2ad462b9dc5b7437320fb2c6fd247c11746b4b2ffd3da95846b9083cad6",
+ "bytes": 555370438,
+ "citation": "ESO/INAF-VST/OmegaCAM eso1119b native TIFF and embedded AVM. The common observation-frame center is an adopted registration origin, not a new dynamical centroid measurement."
+ },
+ {
+ "id": "dsouza-rix-2013",
+ "url": "https://wwwmpa.mpa-garching.mpg.de/mpa/publications/preprints/pp2013/MPA3537.pdf",
+ "sha256": "5701eed16944eb96e0d3e8dea258f909e99a1fa4a5a0305581e878909e70db92",
+ "bytes": 480080,
+ "citation": "D’Souza & Rix (2013), Mass estimates from stellar proper motions: the mass of omega Centauri, Table 1 and section 4.3. Eight projected Gaussian components define a conditional oblate light prior, not resolved stellar depths."
+ }
+ ],
+ "objects": [
+ {
+ "id": "omega-centauri",
+ "kind": "globular-cluster",
+ "name": "Omega Centauri",
+ "aliases": [
+ "NGC 5139",
+ "ω Centauri",
+ "Caldwell 80"
+ ],
+ "positionM": [
+ -105140426158691240000,
+ -41833991150782185000,
+ -123401106146576710000
+ ],
+ "skyPosition": {
+ "raDeg": 201.696958,
+ "decDeg": -47.479539,
+ "sourceRef": "eso1119b-frame"
+ },
+ "distance": {
+ "valuePc": 5426,
+ "minusPc": 47,
+ "plusPc": 47,
+ "sourceRef": "baumgardt-vasiliev-2021",
+ "method": "Combined Gaia EDR3, HST and literature distance, Table 2, NGC 5139. One cluster scale; individual stellar distances are not recovered."
+ },
+ "classification": {
+ "name": "Globular stellar cluster",
+ "basis": "Observed integrated cluster starlight; the selected oblate Gaussian profile supplies conditional depth for finite display-emission components.",
+ "sourceRef": "dsouza-rix-2013"
+ },
+ "status": "confirmed",
+ "detailedObjectId": "omega-centauri"
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/source/presentation.json b/src/objects/omega-centauri/source/presentation.json
new file mode 100644
index 0000000000..cda466d34e
--- /dev/null
+++ b/src/objects/omega-centauri/source/presentation.json
@@ -0,0 +1,148 @@
+{
+ "schema": "cssearth-volume-presentation-source@1",
+ "objectId": "omega-centauri",
+ "name": "Omega Centauri",
+ "defaultLens": "eso1119b",
+ "sharedInputs": [
+ "physical-evidence",
+ "photometric-mge",
+ "distance"
+ ],
+ "lenses": [
+ {
+ "id": "eso1119b",
+ "label": "VST · optical G / R / I",
+ "title": "Omega Centauri in the wider VST field",
+ "description": "Integrated cluster starlight on finite 3D emission components conditioned by a published oblate Gaussian profile. Inclination, near/far tilt and individual feature depths are model assumptions. Both optical lenses share geometry; no duplicate resolved-star layer is added. Colors are relative display emission, not calibrated luminosity or a measured stellar mass map. The original VST image supplies the fitted light distribution. This application integration candidate still fails the fixed image-stability gate at axis handoffs; rotation can reveal changes between slab directions.",
+ "summary": "Integrated optical starlight with conditional oblate depth; individual stellar positions are not recovered.",
+ "detail": "14540 × 14540 px · publisher optical composite",
+ "facts": [
+ {
+ "id": "bands",
+ "label": "Bands",
+ "value": "Optical G (480nm) / R (625nm) / I (770nm)"
+ },
+ {
+ "id": "source-pixels",
+ "label": "Source pixels",
+ "value": "14540 × 14540"
+ },
+ {
+ "id": "coverage",
+ "label": "Image field",
+ "value": "50.88 × 50.88 arcmin"
+ },
+ {
+ "id": "depth",
+ "label": "Depth model",
+ "value": "Published oblate MGE · 50° inclination · 100° major-axis PA"
+ }
+ ],
+ "input": "eso1119b",
+ "preview": {
+ "path": ".local/nebula-lab/observations/omega-centauri/previews/eso1119b-screen.jpg",
+ "sha256": "fde5cacdc6ee88f4dfd62dd77f3ae698cb298a1d9d41cd016f8c407a1cfaeed1",
+ "bytes": 652467,
+ "url": "https://cdn.eso.org/images/screen/eso1119b.jpg"
+ }
+ },
+ {
+ "id": "eso0844a",
+ "label": "WFI · optical B / V / I",
+ "title": "Omega Centauri through the WFI optical mosaic",
+ "description": "Integrated cluster starlight on finite 3D emission components conditioned by a published oblate Gaussian profile. Inclination, near/far tilt and individual feature depths are model assumptions. Both optical lenses share geometry; no duplicate resolved-star layer is added. Colors are relative display emission, not calibrated luminosity or a measured stellar mass map. The WFI image recolors the same fitted geometry over its smaller observed footprint; the publisher reports DSS-filled mosaic gaps. This application integration candidate still fails the fixed image-stability gate at axis handoffs; rotation can reveal changes between slab directions.",
+ "summary": "A second optical view on the same conditional geometry, limited to the WFI mosaic footprint.",
+ "detail": "8040 × 7560 px · publisher optical composite",
+ "facts": [
+ {
+ "id": "bands",
+ "label": "Bands",
+ "value": "Optical B (451nm) / V (539nm) / I (783nm)"
+ },
+ {
+ "id": "source-pixels",
+ "label": "Source pixels",
+ "value": "8040 × 7560"
+ },
+ {
+ "id": "coverage",
+ "label": "Image field",
+ "value": "31.88 × 29.99 arcmin"
+ },
+ {
+ "id": "depth",
+ "label": "Depth model",
+ "value": "Published oblate MGE · 50° inclination · 100° major-axis PA"
+ }
+ ],
+ "input": "eso0844a",
+ "preview": {
+ "path": ".local/nebula-lab/observations/omega-centauri/previews/eso0844a-screen.jpg",
+ "sha256": "2b629fe339d71db3688ac3a4f65041ef1386be6583b37c5b11e71d340cc9a4f4",
+ "bytes": 699356,
+ "url": "https://cdn.eso.org/images/screen/eso0844a.jpg"
+ }
+ }
+ ],
+ "recipes": [
+ {
+ "id": "request",
+ "path": "src/objects/omega-centauri/source/request.json",
+ "sha256": "043c6218aedd90269ec68822cfa2d729d731b7995150c0e01af8b5c68127e241",
+ "bytes": 365
+ },
+ {
+ "id": "recipe-1",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/01-compiler.json",
+ "sha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f",
+ "bytes": 1361
+ },
+ {
+ "id": "recipe-2",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json",
+ "sha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914",
+ "bytes": 2508
+ },
+ {
+ "id": "recipe-3",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/03-observation-structures.json",
+ "sha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e",
+ "bytes": 397
+ },
+ {
+ "id": "recipe-4",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json",
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51",
+ "bytes": 36711
+ },
+ {
+ "id": "recipe-5",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json",
+ "sha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2",
+ "bytes": 3842
+ },
+ {
+ "id": "delivery",
+ "path": "src/objects/omega-centauri/source/delivery.json",
+ "sha256": "d1e34564e98eae6ebe9d8d59b8cfd4ebe19c20e0c14f8279c72444f96faf36a5",
+ "bytes": 2428
+ }
+ ],
+ "inputEvidence": [
+ {
+ "sourceId": "distance",
+ "role": "placement",
+ "evidence": "source/nebula.json#/objects/0/distance: one observed cluster distance locates the scene; individual stellar distances remain unknown."
+ },
+ {
+ "sourceId": "photometric-mge",
+ "role": "geometry",
+ "evidence": "source/bake-inputs/references/02-photometric-mge.json: published projected Gaussian fit and explicit oblate deprojection assumptions."
+ }
+ ],
+ "bank": {
+ "path": "src/objects/omega-centauri/prepared/lenses.json",
+ "sha256": "42121aa3e3a35d16034954fb982b43a693d35d8724165e388f2a5f7d6087495c",
+ "bytes": 433153
+ }
+}
diff --git a/src/objects/omega-centauri/source/provenance-references.json b/src/objects/omega-centauri/source/provenance-references.json
new file mode 100644
index 0000000000..f01d7ff962
--- /dev/null
+++ b/src/objects/omega-centauri/source/provenance-references.json
@@ -0,0 +1,40 @@
+{
+ "schema": "cssearth-provenance-references@1",
+ "references": [
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/compiler.json",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/01-compiler.json",
+ "sha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f",
+ "bytes": 1361
+ },
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/photometric-mge.json",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/02-photometric-mge.json",
+ "sha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914",
+ "bytes": 2508
+ },
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/observation-structures.json",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/03-observation-structures.json",
+ "sha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e",
+ "bytes": 397
+ },
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/physical-evidence.json",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/04-physical-evidence.json",
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51",
+ "bytes": 36711
+ },
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/observations.json",
+ "revision": "862cdbed96bbca43cdf1f13549c94e4ca501add3",
+ "path": "src/objects/omega-centauri/source/bake-inputs/references/05-observations.json",
+ "sha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2",
+ "bytes": 3842
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/source/replay-references.json b/src/objects/omega-centauri/source/replay-references.json
new file mode 100644
index 0000000000..4558194d76
--- /dev/null
+++ b/src/objects/omega-centauri/source/replay-references.json
@@ -0,0 +1,30 @@
+{
+ "schema": "cssearth-replay-references@1",
+ "references": [
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/compiler.json",
+ "path": "source/bake-inputs/references/01-compiler.json",
+ "sha256": "37c12a3be28a8dfbebb8931cd5f528673b63018c34aea8a4a07b23b26b6a1f3f"
+ },
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/photometric-mge.json",
+ "path": "source/bake-inputs/references/02-photometric-mge.json",
+ "sha256": "4e28dac4271eeafd77f77582cfd69105ca02677e6bbe6c88e583f412c7b27914"
+ },
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/observation-structures.json",
+ "path": "source/bake-inputs/references/03-observation-structures.json",
+ "sha256": "dc8dd69f0a7f1c15d8b6376e43147e3e2cd2c52ab802d7fc5651d23c28ae8c0e"
+ },
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/physical-evidence.json",
+ "path": "source/bake-inputs/references/04-physical-evidence.json",
+ "sha256": "6e88b6abe042cb66a104931cf1039fb3e6d685dd308b303226bdb6e530ac5b51"
+ },
+ {
+ "originalPath": "labs/nebula/models/omega-centauri/observations.json",
+ "path": "source/bake-inputs/references/05-observations.json",
+ "sha256": "5954f0098ffe25ad48ff22b581f6d47cbc67e7d902e5d1f5ac19822c0612b9c2"
+ }
+ ]
+}
diff --git a/src/objects/omega-centauri/source/request.json b/src/objects/omega-centauri/source/request.json
new file mode 100644
index 0000000000..a75d7d7bd6
--- /dev/null
+++ b/src/objects/omega-centauri/source/request.json
@@ -0,0 +1,16 @@
+{
+ "action": "apply",
+ "imageId": "compiler",
+ "recipePath": "labs/nebula/models/omega-centauri/compiler.json",
+ "cataloguePath": ".local/nebula-lab/observations/omega-centauri/structures/catalogue.json",
+ "controls": {
+ "detail": 1,
+ "faint": 0.35,
+ "depth": 1
+ },
+ "imageToFrame": {},
+ "evidence": {
+ "sensitivity": 1,
+ "weights": []
+ }
+}
diff --git a/src/renderers/css/sky/prepared-sky-runtime.test.ts b/src/renderers/css/sky/prepared-sky-runtime.test.ts
index 3bc61ac7b5..7871076f37 100644
--- a/src/renderers/css/sky/prepared-sky-runtime.test.ts
+++ b/src/renderers/css/sky/prepared-sky-runtime.test.ts
@@ -9,6 +9,7 @@ import type { PreparedVolumeLenses } from '../volume/prepared-volume-lenses.js';
import { validatePreparedCssVolume } from '../volume/validation.js';
import { preparedVolumeCameraTransform } from '../volume/prepared-volume-runtime.js';
import { worldRotationCss } from '../navigation/world-camera-math.js';
+import type { PreparedNavigationFocus } from '../navigation/prepared-focus.js';
import type { WorldCameraPose } from '../navigation/world-camera.js';
import { createPreparedUniverse } from '../universe/prepared-universe-runtime.js';
import { logarithmicFade } from '../universe/prepared-world-context.js';
@@ -411,7 +412,7 @@ test('selecting a nebula loads its bank on demand even while it is out of view',
resources: ['x', 'y', 'z'].map(axis => ({ path: `${axis}.webp`, sha256: 'a'.repeat(64), bytes: 1, width: 1, height: 1 })),
provenance: {}, approximation: {} };
const bank: PreparedVolumeLenses = { schema: 'cssearth-volume-lenses@1', id: 'far-nebula', defaultLens: 'optical',
- framingRadiusUnits: 1, contextVisibility: 'independent', lenses: [{ id: 'optical', label: 'Optical', title: 'Optical emission',
+ framingRadiusUnits: .25, contextVisibility: 'independent', lenses: [{ id: 'optical', label: 'Optical', title: 'Optical emission',
description: 'Prepared far nebula', sourceUrl: 'https://example.org/nebula', volume: nebula,
brightness: { overall: 1, x: 1, y: 1, z: 1 }, stars: { frame, points: [] } }] };
const document = new FakeDocument(), stage = document.createElement();
@@ -426,11 +427,24 @@ test('selecting a nebula loads its bank on demand even while it is out of view',
mounted.publish(far, viewport);
expect(loadVolumeLens).not.toHaveBeenCalled();
expect(mounted.volumeLensState(bank.id)).toBeNull();
- // Selecting it, e.g. from the catalogue, must trigger the load without waiting for proximity.
+ expect(mounted.volumeLensFrames[bank.id]!.framingRadiusUnits).toBe(Math.hypot(1, 1, 1));
+ // Focus readiness and dataset selection share the same real loader work.
+ const readiness = mounted.ensureVolumeLens(bank.id);
mounted.selectVolumeLens(bank.id, 'optical');
expect(loadVolumeLens).toHaveBeenCalledExactlyOnceWith(bank.id);
- await vi.waitFor(() => expect(mounted.volumeLensState(bank.id)).not.toBeNull());
+ await readiness;
+ expect(mounted.volumeLensState(bank.id)).not.toBeNull();
+ expect(mounted.volumeLensFrames[bank.id]!.framingRadiusUnits).toBe(.25);
expect(mounted.volumeLensState(bank.id)!.selectedLens).toBe('optical');
+ const failure = new Error('Bank download failed');
+ const failed = createPreparedUniverse({ context, volume, pointAppearance: readCanonicalPointField(), sprites: {},
+ resolveResource: path => `/volume/${path}`, resolvePointResource: path => `/stars/${path}`,
+ volumeLensBanks: [{ id: bank.id, frame }], loadVolumeLens: () => Promise.reject(failure) }).mount(document.createElement() as unknown as HTMLElement);
+ try {
+ await expect(failed.ensureVolumeLens(bank.id)).rejects.toBe(failure);
+ expect(failed.volumeLensState(bank.id)).toBeNull();
+ await expect(failed.ensureVolumeLens('unknown')).rejects.toThrow('Unknown prepared volume lens bank');
+ } finally { failed.destroy(); }
} finally { mounted.destroy(); }
});
@@ -540,3 +554,91 @@ test("baked stars hand the background to the plain Milky Way beyond the Sun's ne
expect(leaves(stars)[0]!.parentNode!.style.transform).toBe(preparedSkyCameraTransform(world([0,0,0], [0,Math.SQRT1_2,0,Math.SQRT1_2]), viewport));
expect(document.count).toBe(count);
});
+
+
+test('authoritative detailed close-up gates background fetch, painting and publication without changing the NASA sky weight', async () => {
+ vi.stubGlobal('HTMLElement', FakeElement); vi.stubGlobal('Element', FakeElement);
+ const base = new URL('../../../', import.meta.url), parsecM = 3.085677581491367e16;
+ const context = JSON.parse(readFileSync(new URL('objects/sun/prepared/world-context.json', base), 'utf8'));
+ context.volume.opacityProfile = { model: 'logarithmic-distance', fadeStartDistanceM: 1e20, fullDistanceM: 1e22, nearOpacity: 0, fullOpacity: 1 };
+ const volume = JSON.parse(readFileSync(new URL('objects/milky-way/prepared/volume.json', base), 'utf8')).data as PreparedCssVolume;
+ const frame: PreparedCssVolume['frame'] = { referenceFrame: volume.frame.referenceFrame, epochJdTt: volume.frame.epochJdTt,
+ originM: [context.focus.positionM[0], context.focus.positionM[1], context.focus.positionM[2] + 5000 * parsecM],
+ localToReferenceXyzw: [0, 0, 0, 1], metersPerUnit: .1 * parsecM, boundsUnits: { min: [-1, -1, -1], max: [1, 1, 1] } };
+ const small: PreparedCssVolume = { schema: 'cssearth-css-volume@1', id: 'small', frame, anchors: [],
+ stacks: (['x', 'y', 'z'] as const).map(axis => ({ axis, leaves: [{ id: `${axis}-0`, centerUnits: [0, 0, 0],
+ texturePath: `${axis}.webp`, widthPx: 1, heightPx: 1,
+ style: { width: '1px', height: '1px', transform: 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)',
+ backgroundSize: '1px 1px', backgroundPosition: '0px 0px' } }] })),
+ resources: ['x', 'y', 'z'].map(axis => ({ path: `${axis}.webp`, sha256: 'a'.repeat(64), bytes: 1, width: 1, height: 1 })), provenance: {}, approximation: {} };
+ const bank = (id: string): PreparedVolumeLenses => ({ schema: 'cssearth-volume-lenses@1', id, defaultLens: 'optical',
+ framingRadiusUnits: 1, contextVisibility: 'independent', lenses: [{ id: 'optical', label: 'Optical', title: 'Optical emission',
+ description: 'Prepared fixture', sourceUrl: 'https://example.org/nebula', volume: { ...small, id },
+ brightness: { overall: 1, x: 1, y: 1, z: 1 }, stars: { frame, points: [] } }] });
+ const banks = ['focus-bank', 'warm-bank', 'cold-bank'].map(bank);
+ const image: PreparedCssImageLayers = { ...small, id: 'image-bank', bankViews: small.stacks.map(stack => ({ axis: stack.axis,
+ normalUnits: stack.axis === 'x' ? [1, 0, 0] : stack.axis === 'y' ? [0, 1, 0] : [0, 0, 1], samplingStepUnits: 1 })) };
+ const loadVolumeLens = vi.fn(async (id: string) => ({ payload: banks.find(bank => bank.id === id)!, resolveResource: (path: string) => `/bank/${id}/${path}` }));
+ const loadImageLayer = vi.fn(async () => ({ payload: image, resolveResource: (path: string) => `/image/${path}` }));
+ catalogMount.mockReturnValue({ destroy() {}, select() {}, resolve: (id: string) => ({ id, detailedObjectId: id === 'catalogue-only' ? undefined : id.replace('catalogue:', '') }), publish() {}, inspect() {} });
+ const document = new FakeDocument(), stage = document.createElement();
+ const fetchResource = vi.fn(async () => new Response(new Uint8Array([1])));
+ Object.assign(document.defaultView, { fetch: fetchResource });
+ const mounted = createPreparedUniverse({ context, volume, pointAppearance: readCanonicalPointField(), sprites: {},
+ resolveResource: path => `/volume/${path}`, resolvePointResource: path => `/stars/${path}`,
+ volumeLensBanks: banks.map(bank => ({ id: bank.id, frame })), loadVolumeLens,
+ imageLayerBanks: [{ id: image.id, frame }], loadImageLayer, catalog: { payload: {}, fadeStartDistanceM: 10, fullDistanceM: 20 },
+ }).mount(stage as unknown as HTMLElement);
+ const root = mounted.root as unknown as FakeElement;
+ const findBank = (id: string) => root.children.find(node => node.dataset.volumeLensObject === id)!;
+ const mw = root.children.find(node => node.className === 'prepared-volume-context')!;
+ const sky = root.children.find(node => node.className === 'prepared-celestial-sky')!;
+ const stellar = root.children.find(node => node.className === 'stellar-direct-points')!;
+ const focus = (id = 'catalogue:focus-bank'): PreparedNavigationFocus => ({ id, positionM: frame.originM, framingRadiusM: frame.metersPerUnit,
+ limits: { minimumDistanceM: .01 * frame.metersPerUnit, maximumDistanceM: 1e25 } });
+ const camera = (radii: number): WorldCameraPose => ({ referenceFrame: frame.referenceFrame, epochJdTt: frame.epochJdTt,
+ pose: { positionM: [frame.originM[0], frame.originM[1], frame.originM[2] + radii * frame.metersPerUnit], orientationXyzw: [0, 0, 0, 1] } });
+ try {
+ await mounted.ensureVolumeLens('focus-bank'); await mounted.ensureVolumeLens('warm-bank');
+ mounted.selectGalaxy('catalogue:focus-bank', focus());
+ const near = camera(6.1), savedPose = structuredClone(near);
+ mounted.publish(near, viewport);
+ expect(loadVolumeLens.mock.calls.map(([id]) => id)).toEqual(['focus-bank', 'warm-bank']);
+ expect(loadImageLayer).not.toHaveBeenCalled(); expect(fetchResource).not.toHaveBeenCalled();
+ expect(findBank('focus-bank').style.display).toBe('block');
+ expect(findBank('warm-bank').style.display).toBe('none'); expect(mw.style.display).toBe('none');
+ expect(near).toEqual(savedPose);
+ const all = (node: FakeElement): FakeElement[] => [node, ...node.children.flatMap(all)];
+ expect(all(findBank('warm-bank')).some(node => node.style.backgroundImage)).toBe(false);
+ for (const [radii, multiplier] of [[6.1, 0], [20, .5], [32, 1]] as const) {
+ mounted.publish(camera(radii), viewport);
+ if (radii > 8) { await mounted.ensureVolumeLens('cold-bank'); await mounted.ensureImageLayer(image.id); mounted.publish(camera(radii), viewport); }
+ const originalOpacity = Number(mw.dataset.volumeOpacity), alpha = Number(mw.style.opacity);
+ expect(originalOpacity).toBeGreaterThan(0); expect(originalOpacity).toBeLessThan(1);
+ const completedContribution = originalOpacity * Number(mw.children[0]!.dataset.volumeBrightness);
+ expect(alpha).toBeCloseTo(completedContribution * multiplier, 12);
+ expect((1 - alpha) * Number(sky.style.opacity)).toBeCloseTo(1 - completedContribution, 12);
+ expect(Number(sky.dataset.skyContribution)).toBeCloseTo(1 - completedContribution, 12);
+ expect(Number(stellar.style.opacity)).toBeCloseTo(STELLAR_POINTS_MAX_OPACITY * (1 - completedContribution) * multiplier, 12);
+ expect(stellar.style.display).toBe(multiplier > 0 ? 'block' : 'none');
+ mounted.setStellarPointsEnabled(false); mounted.setStellarPointsEnabled(true);
+ expect(stellar.style.display).toBe(multiplier > 0 ? 'block' : 'none');
+ const selectedOpacity = Number(findBank('focus-bank').style.opacity);
+ expect(selectedOpacity).toBeGreaterThan(0);
+ expect(Number(findBank('warm-bank').style.opacity)).toBeCloseTo(multiplier * selectedOpacity, 12);
+ }
+ const imageRoot = root.children.find(node => node.dataset.imageLayerObject === image.id)!;
+ mounted.publish(near, viewport);
+ expect(imageRoot.style.display).toBe('none');
+ const before = all(findBank('warm-bank')).map(node => ({ ...node.style }));
+ mounted.publish({ ...near, pose: { ...near.pose, orientationXyzw: [0, Math.SQRT1_2, 0, Math.SQRT1_2] } }, viewport);
+ expect(all(findBank('warm-bank')).map(node => ({ ...node.style }))).toEqual(before);
+ mounted.selectGalaxy('catalogue:image-bank', focus('catalogue:image-bank')); mounted.publish(near, viewport);
+ expect(imageRoot.style.display).toBe(''); expect(findBank('focus-bank').style.display).toBe('none');
+ mounted.selectGalaxy(null); mounted.publish(near, viewport);
+ expect(mw.style.display).toBe(''); expect(findBank('focus-bank').style.display).toBe('block');
+ mounted.selectGalaxy('catalogue-only', focus('catalogue-only')); mounted.publish(near, viewport);
+ expect(mw.style.display).toBe('');
+ expect(() => mounted.selectGalaxy('catalogue:focus-bank', focus('mismatch'))).toThrow('focus');
+ } finally { mounted.destroy(); }
+});
diff --git a/src/renderers/css/universe/catalog-marker.test.ts b/src/renderers/css/universe/catalog-marker.test.ts
index 0ec96221aa..1157395765 100644
--- a/src/renderers/css/universe/catalog-marker.test.ts
+++ b/src/renderers/css/universe/catalog-marker.test.ts
@@ -1,7 +1,7 @@
import { readFileSync } from 'node:fs';
import { expect, test } from 'vitest';
import { parsePreparedNebulaCatalog, parsePreparedGalaxyCatalog, parsePreparedClusterCatalog } from '@cssearth/catalog';
-import { catalogMarkerKind, mountCatalogMarkerKind } from './catalog-marker.js';
+import { catalogMarkerKind, catalogMarkerSvg, mountCatalogMarkerKind } from './catalog-marker.js';
test('shipped classifications select the correct marker without object ID exceptions', () => {
for (const [id, expected] of [['m45', 'open-cluster'], ['helix', 'planetary-nebula'],
@@ -19,7 +19,7 @@ test('outer scene markers share the ordinary circle while retaining classificati
const clusters = parsePreparedClusterCatalog(JSON.parse(readFileSync(
new URL('../../../objects/galaxy-clusters/prepared/catalogue.json', import.meta.url), 'utf8')));
expect(clusters.objects.every(object => catalogMarkerKind(object) === 'galaxy-cluster')).toBe(true);
- for (const kind of ['galaxy', 'nebula', 'planetary-nebula', 'open-cluster', 'galaxy-group', 'galaxy-cluster'] as const) {
+ for (const kind of ['galaxy', 'nebula', 'planetary-nebula', 'open-cluster', 'globular-cluster', 'galaxy-group', 'galaxy-cluster'] as const) {
const marker = { className: '', dataset: { catalogMarkerKind: '' }, style: {}, innerHTML: '', setAttribute() {} };
mountCatalogMarkerKind(marker as unknown as HTMLElement, kind);
expect(marker.dataset.catalogMarkerKind).toBe(kind);
@@ -27,3 +27,12 @@ test('outer scene markers share the ordinary circle while retaining classificati
expect(marker.innerHTML).toBe('');
}
});
+
+test('globular-cluster kind selects a stellar-cluster marker independently of its display name', () => {
+ const catalog = parsePreparedNebulaCatalog(JSON.parse(readFileSync(
+ new URL('../../../objects/m45/source/nebula.json', import.meta.url), 'utf8')));
+ const cluster = { ...catalog.objects[0]!, kind: 'globular-cluster' as const };
+ expect(catalogMarkerKind(cluster)).toBe('globular-cluster');
+ expect(catalogMarkerSvg('globular-cluster')).toContain(' = {
nebula: cloud,
'planetary-nebula': cloud + '',
'open-cluster': '',
+ 'globular-cluster': '',
'galaxy-group': '',
'galaxy-cluster': '',
};
diff --git a/src/renderers/css/universe/detailed-focus-context.test.ts b/src/renderers/css/universe/detailed-focus-context.test.ts
new file mode 100644
index 0000000000..b2893f782e
--- /dev/null
+++ b/src/renderers/css/universe/detailed-focus-context.test.ts
@@ -0,0 +1,17 @@
+import { expect, test } from 'vitest';
+import { detailedFocusContextOpacity } from './detailed-focus-context.js';
+import type { WorldCameraPose } from '../navigation/world-camera.js';
+
+const focus = { positionM: [1e12, -2e12, 3e12] as const, framingRadiusM: 1e6 };
+const world = (radii: number): WorldCameraPose => ({ referenceFrame: 'fixture', epochJdTt: 1,
+ pose: { positionM: [focus.positionM[0], focus.positionM[1], focus.positionM[2] + radii * focus.framingRadiusM], orientationXyzw: [0, 0, 0, 1] } });
+test('close-up context covers native arrival, smoothly restores in physical focus radii, and leaves absent focus unchanged', () => {
+ for (const distance of [0, .05, 6.1, 8]) expect(detailedFocusContextOpacity(world(distance), focus)).toBe(0);
+ expect(detailedFocusContextOpacity(world(20), focus)).toBe(.5);
+ for (const distance of [32, 100]) expect(detailedFocusContextOpacity(world(distance), focus)).toBe(1);
+ const samples = Array.from({ length: 321 }, (_, index) => detailedFocusContextOpacity(world(index / 10), focus));
+ expect(samples.every((value, index) => index === 0 || value >= samples[index - 1]!)).toBe(true);
+ const camera = world(6.1), before = structuredClone(camera);
+ expect(detailedFocusContextOpacity(camera, null)).toBe(1);
+ expect(camera).toEqual(before);
+});
diff --git a/src/renderers/css/universe/detailed-focus-context.ts b/src/renderers/css/universe/detailed-focus-context.ts
new file mode 100644
index 0000000000..6dffb6b18b
--- /dev/null
+++ b/src/renderers/css/universe/detailed-focus-context.ts
@@ -0,0 +1,15 @@
+import type { PreparedNavigationFocus } from '../navigation/prepared-focus.js';
+import type { WorldCameraPose } from '../navigation/world-camera.js';
+
+/** Presentation only: normal focus arrival is about 6.1 authored radii. */
+const HIDDEN_WITHIN_RADII = 8;
+const RESTORED_BY_RADII = 32;
+
+/** Fade surrounding detailed banks, without changing the observer or the Sun-distance sky policy. */
+export function detailedFocusContextOpacity(world: WorldCameraPose,
+ focus: Pick | null): number {
+ if (!focus) return 1;
+ const radii = Math.hypot(...world.pose.positionM.map((value, axis) => value - focus.positionM[axis])) / focus.framingRadiusM;
+ const progress = Math.max(0, Math.min(1, (radii - HIDDEN_WITHIN_RADII) / (RESTORED_BY_RADII - HIDDEN_WITHIN_RADII)));
+ return progress * progress * (3 - 2 * progress);
+}
diff --git a/src/renderers/css/universe/prepared-universe-runtime.ts b/src/renderers/css/universe/prepared-universe-runtime.ts
index a3b207ee8f..f7b7389a00 100644
--- a/src/renderers/css/universe/prepared-universe-runtime.ts
+++ b/src/renderers/css/universe/prepared-universe-runtime.ts
@@ -16,7 +16,9 @@ import { mountPreparedCssSky } from '../sky/prepared-sky-runtime.js';
import { mountEnvironmentLabels } from './environment-labels.js';
import { mountPreparedGalaxyCatalog } from './prepared-galaxy-catalog.js';
import { mountPreparedCssImageLayers } from '../image-layers/prepared-image-layer-runtime.js';
-import type { PreparedCatalogObject } from '@cssearth/catalog';
+import { isPreparedCluster, type PreparedCatalogObject } from '@cssearth/catalog';
+import type { PreparedNavigationFocus } from '../navigation/prepared-focus.js';
+import { detailedFocusContextOpacity } from './detailed-focus-context.js';
import type { PreparedCssImageLayers } from '../image-layers/loader.js';
import { createPreparedVolumeLenses } from '../volume/prepared-volume-lenses.js';
import type { PreparedVolumeLenses } from '../volume/prepared-volume-lenses.js';
@@ -333,6 +335,8 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
const shellLayers: ReturnType[] = [];
const mountedShells = [...shells];
let selected = plan.focus;
+ let detailedFocus: { objectId: string; focus: PreparedNavigationFocus } | null = null;
+ let detailContextOpacity = 1;
let destroyed = false;
let volumeOpacity = 0, volumeBrightness = 1, volumeSize = 1;
let publishedVolumeAlpha = NaN, publishedImageAlpha = NaN, publishedSkyAlpha = NaN;
@@ -344,15 +348,15 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
let starsHandoff = 0;
const publishBackground = () => {
const brightness = volumeBrightness;
- // The volume's prepared exposure is part of its visible contribution. Keep the
- // opaque sky underneath it until that completed contribution actually replaces it;
- // fading the sky by raw volume opacity leaves a black trough while b is still low.
- const alpha = skyLayer ? volumeOpacity * brightness : volumeOpacity;
+ // Preserve the exposure-aware sky handoff while a detailed focus suppresses
+ // the surrounding volume. Its suppression must not brighten the sky behind it.
+ const completedContribution = skyLayer ? volumeOpacity * brightness : volumeOpacity;
+ const alpha = completedContribution * detailContextOpacity;
if (alpha !== publishedVolumeAlpha) {
volumeHost.style.opacity = String(alpha);
- if (skyLayer) skyLayer.root.dataset.skyContribution = String(1 - alpha);
publishedVolumeAlpha = alpha;
}
+ if (skyLayer) skyLayer.root.dataset.skyContribution = String(1 - completedContribution);
// The galaxy's own slices fade with its projected size; the matte and sky handoff do not.
const imageAlpha = (skyLayer ? 1 : brightness) * volumeSize;
if (imageAlpha !== publishedImageAlpha) {
@@ -360,7 +364,7 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
volumeImage.style.display = imageAlpha > 0 ? '' : 'none';
publishedImageAlpha = imageAlpha;
}
- const skyAlpha = 1;
+ const skyAlpha = alpha < 1 ? (1 - completedContribution) / (1 - alpha) : 0;
if (skyLayer && skyAlpha !== publishedSkyAlpha) { skyLayer.root.style.opacity = String(skyAlpha); publishedSkyAlpha = skyAlpha; }
};
const destroy = () => {
@@ -413,13 +417,33 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
environmentLabels!.addShell(shell.payload);
requestPublication?.();
},
- selectGalaxy(id: string | null) { galaxyCatalog?.select(id); },
+ selectGalaxy(id: string | null, focus: PreparedNavigationFocus | null = null) {
+ if (focus && (focus.id !== id || !Number.isFinite(focus.framingRadiusM) || focus.framingRadiusM <= 0 ||
+ focus.positionM.length !== 3 || !focus.positionM.every(Number.isFinite))) {
+ throw new TypeError('Prepared context focus must match its selection and have finite authored framing.');
+ }
+ const record = id ? galaxyCatalog?.resolve(id) : null;
+ const objectId = record && !isPreparedCluster(record) ? record.detailedObjectId : undefined;
+ const next = focus && objectId && [...declaredImageLayers, ...volumeLensBanks].some(bank => bank.id === objectId)
+ ? { objectId, focus } : null;
+ const changed = next?.objectId !== detailedFocus?.objectId || next?.focus.framingRadiusM !== detailedFocus?.focus.framingRadiusM ||
+ next?.focus.positionM.some((value, axis) => value !== detailedFocus?.focus.positionM[axis]);
+ detailedFocus = next;
+ galaxyCatalog?.select(id);
+ if (changed) requestPublication?.();
+ },
resolveGalaxy(id: string) { return galaxyCatalog?.resolve(id) ?? null; },
ensureGalaxyCatalog: ensureCatalogLoaded,
ensureImageLayer(id: string) {
const index = declaredImageLayers.findIndex(bank => bank.id === id);
return index < 0 ? Promise.resolve() : ensureImageLayerLoaded(index);
},
+ /** Await the shared loader so focus consumers can use the payload's authored framing and report failures. */
+ ensureVolumeLens(id: string) {
+ const index = volumeLensBanks.findIndex(bank => bank.id === id);
+ if (index < 0) return Promise.reject(new TypeError('Unknown prepared volume lens bank.'));
+ return ensureLensLoaded(index);
+ },
imageLayerFrames: Object.freeze(Object.fromEntries(declaredImageLayers.map(bank => [bank.id, bank.frame]))),
// Framing is exact once a bank's payload has loaded; until then it falls back to the radius that
// holds the descriptor's own prepared bounds, the same estimate image-layer banks always use.
@@ -537,14 +561,15 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
if (destroyed) return;
opacityClock.batch(() => {
const distanceM = Math.hypot(...world.pose.positionM.map((value, axis) => value - plan.focus.positionM[axis]));
- prefetchGalaxy(distanceM);
+ detailContextOpacity = detailedFocusContextOpacity(world, detailedFocus?.focus ?? null);
+ if (detailContextOpacity > 0) prefetchGalaxy(distanceM);
additionalPoints.publish({world, viewport}, distanceM);
const fade = logarithmicFade(distanceM, plan.volume.fadeStartDistanceM, plan.volume.fullDistanceM);
starsHandoff = logarithmicFade(distanceM, plan.stars.fadeStartDistanceM, plan.stars.fullDistanceM);
volumeOpacity = preparedVolumeOpacity(distanceM, plan.volume.opacityProfile);
volumeBrightness = preparedVolumeOpacity(distanceM, plan.volume.brightnessProfile);
volumeSize = projectedVolumeOpacity(world, viewport, payload.frame, volumeFramingUnits);
- const volumeVisible = volumeOpacity > 0;
+ const volumeVisible = volumeOpacity * detailContextOpacity > 0;
if (volumeVisible !== publishedVolumeVisible) { volumeHost.style.display = volumeVisible ? '' : 'none'; publishedVolumeVisible = volumeVisible; }
if (volumeOpacity !== publishedVolumeOpacity) {
volumeHost.dataset.volumeOpacity = String(volumeOpacity);
@@ -554,15 +579,17 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
volumeImage.dataset.volumeBrightness = String(volumeBrightness); publishedVolumeBrightness = volumeBrightness;
}
publishBackground();
- skyLayer?.publish(world, viewport, publishedVolumeAlpha < 1, 1 - starsHandoff);
+ const completedContribution = skyLayer ? volumeOpacity * volumeBrightness : volumeOpacity;
+ skyLayer?.publish(world, viewport, completedContribution < 1, 1 - starsHandoff);
stellarPublication = { world, viewport };
- stellarOpacity = stellarPointsOpacity(starsHandoff, publishedVolumeAlpha);
+ stellarOpacity = stellarPointsOpacity(starsHandoff, completedContribution) * detailContextOpacity;
stellarPoints?.publish(stellarPublication, stellarPointsEnabled ? stellarOpacity : 0);
- if (volumeOpacity > 0 && volumeSize > 0) volumeLayer!.publish({ world, viewport });
+ if (volumeVisible && volumeSize > 0) volumeLayer!.publish({ world, viewport });
// A galaxy under a few projected pixels is its label: its bank fades, then
// leaves layout and compositing. Like lens banks, a faded image bank does too.
for (const [index, bank] of imageBanks.entries()) {
- const opacity = volumeOpacity * projectedVolumeOpacity(world, viewport, declaredImageLayers[index]!.frame, imageFramingUnits[index]!);
+ const presentationOpacity = declaredImageLayers[index]!.id === detailedFocus?.objectId ? 1 : detailContextOpacity;
+ const opacity = presentationOpacity * volumeOpacity * projectedVolumeOpacity(world, viewport, declaredImageLayers[index]!.frame, imageFramingUnits[index]!);
if (!bank) {
if (opacity > 0) void ensureImageLayerLoaded(index).catch(() => {});
continue;
@@ -577,6 +604,7 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
let lensResidencyChanged = false;
for (const [index, bank] of lensBanks.entries()) {
const { frame, radiusUnits, visibility } = lensFraming[index]!;
+ const presentationOpacity = volumeLensBanks[index]!.id === detailedFocus?.objectId ? 1 : detailContextOpacity;
if (!bank) {
// The fetch gate and the render gate are deliberately different. Rendering multiplies by
// contextOpacity (the general galactic fade, 'galactic' by default) below, once the payload
@@ -586,7 +614,7 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
// proximity at all, only by explicit selection. Fetching a 'galactic' bank a little earlier
// than its fade would have shown it is cheap; never fetching an 'independent' one is a blank
// nebula.
- const visible = lensEnabled[index] && projectedVolumeOpacity(world, viewport, frame, radiusUnits, visibility) > 0;
+ const visible = lensEnabled[index] && presentationOpacity > 0 && projectedVolumeOpacity(world, viewport, frame, radiusUnits, visibility) > 0;
if (visible !== lensVisible[index]) {
lensVisible[index] = visible; lensLastUsed[index] = ++lensUseClock; lensResidencyChanged = true;
}
@@ -596,7 +624,7 @@ export function createPreparedUniverse({ context, volume, pointAppearance, resol
continue;
}
const contextOpacity = lensPayload[index]!.contextVisibility === 'independent' ? 1 : volumeOpacity;
- const opacity = lensEnabled[index] ? contextOpacity * projectedVolumeOpacity(world, viewport, frame, radiusUnits, visibility) : 0;
+ const opacity = lensEnabled[index] ? presentationOpacity * contextOpacity * projectedVolumeOpacity(world, viewport, frame, radiusUnits, visibility) : 0;
const visible = opacity > 0;
if (visible !== lensVisible[index]) {
lensVisible[index] = visible; lensLastUsed[index] = ++lensUseClock; lensResidencyChanged = true;
diff --git a/src/renderers/css/volume/compiler-render-budget.ts b/src/renderers/css/volume/compiler-render-budget.ts
new file mode 100644
index 0000000000..048cd1eb8e
--- /dev/null
+++ b/src/renderers/css/volume/compiler-render-budget.ts
@@ -0,0 +1,8 @@
+import type { RenderElementProfile } from '@cssearth/volume-core/contracts/render-element-budget';
+
+/** One topology, at most 26 impostors, no separate occulting root. Conformance tests count the actual mounted DOM.
+ * Current inclusive overhead is 46 elements; reserve one more element before allocating the retained XYZ slabs. */
+export const CSS_COMPILER_RENDER_BUDGET: Readonly = Object.freeze({
+ schema: 'cssearth-render-element-profile@1', id: 'css-volume-single-topology@1', maximumElements: 500,
+ elementsPerSlab: 3, elementsPerStar: 1, reservedElements: 47,
+});
diff --git a/src/renderers/css/volume/prepared-volume-lenses.test.ts b/src/renderers/css/volume/prepared-volume-lenses.test.ts
index 7f62d99779..7ee7dd8d75 100644
--- a/src/renderers/css/volume/prepared-volume-lenses.test.ts
+++ b/src/renderers/css/volume/prepared-volume-lenses.test.ts
@@ -10,6 +10,8 @@ import type { PreparedCataloguePoints } from '../stars/prepared-catalogue-points
import { prepareObjectResources } from '../runtime/prepared-resource-lease.js';
import { createPreparedResidency } from '../rendering/prepared-residency.js';
import { cloudCompositeOpacity } from '@cssearth/volume-viewer/scene/cloud-inspection';
+import { CSS_COMPILER_RENDER_BUDGET } from './compiler-render-budget.js';
+import { createRenderElementBudget } from '@cssearth/volume-core/contracts/render-element-budget';
class FakeElement {
readonly nodeType = 1;
@@ -68,6 +70,64 @@ function dom() {
const descendants = (root: FakeElement): FakeElement[] => [root, ...root.children.flatMap(descendants)];
afterEach(() => vi.unstubAllGlobals());
+/** A complete retained topology, including the 26 camera directions used by app preparation. */
+function budgetPayload(slabCount: number, starCount: number): PreparedVolumeLenses {
+ const normalize = (v: VolumeVector): VolumeVector => {
+ const length = Math.hypot(...v); return [v[0] / length, v[1] / length, v[2] / length];
+ };
+ const cross = (a: VolumeVector, b: VolumeVector): VolumeVector =>
+ [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
+ const directions: { id: string; back: VolumeVector; right: VolumeVector; down: VolumeVector }[] = [];
+ for (const x of [-1, 0, 1]) for (const y of [-1, 0, 1]) for (const z of [-1, 0, 1]) {
+ if (!x && !y && !z) continue;
+ const back = normalize([x, y, z]), right = normalize(cross(Math.abs(back[1]) > .99 ? [0, 0, 1] : [0, 1, 0], back));
+ directions.push({ id: `view-${directions.length}`, back, right, down: cross(right, back) });
+ }
+ const original = payload();
+ return { ...original, starsEnabled: false, lenses: original.lenses.map(lens => {
+ const source = lens.volume;
+ const views = directions.map(view => ({ ...view, texturePath: `${lens.id}/${view.id}.png` }));
+ return { ...lens, stars: { frame, points: points().points.slice(0, starCount) }, volume: { ...source,
+ stacks: source.stacks.map((stack, axis) => ({ ...stack,
+ leaves: Array.from({ length: Math.floor(slabCount / 3) + (axis < slabCount % 3 ? 1 : 0) }, (_, index) =>
+ ({ ...stack.leaves[0]!, id: `${stack.axis}-${index}` })) })),
+ impostors: { schema: 'cssearth-volume-impostors@1', radiusUnits: 1,
+ fullBelowDiameterPixels: 16, volumeAboveDiameterPixels: 32, views },
+ resources: [...source.resources, ...views.map(view => ({ path: view.texturePath, sha256: 'b'.repeat(64), bytes: 1, width: 1, height: 1 }))],
+ } };
+ }) };
+}
+
+test.each([[151, 0], [150, 3]])('the compiler DOM quota includes every retained XYZ copy, star and impostor (%i slabs, %i stars)', (slabCount, starCount) => {
+ const f = dom(), data = budgetPayload(slabCount, starCount);
+ const runtime = createPreparedVolumeLenses({ payload: data, resolveResource: path => `/prepared/${path}` }).mount(f.options);
+ const root = runtime.root as unknown as FakeElement, initial = descendants(root);
+ const predicted = createRenderElementBudget(CSS_COMPILER_RENDER_BUDGET, starCount, slabCount).totalElements;
+ expect(data.lenses[0]!.volume.stacks.map(stack => stack.leaves.length).reduce((a, b) => a + b, 0)).toBe(slabCount);
+ expect(initial.filter(node => node.parentNode?.className === 'css-volume-mesh').length).toBe(slabCount * 3);
+ expect(initial.filter(node => node.dataset.volumeImpostor !== undefined).length).toBe(26);
+ expect(initial.filter(node => node.dataset.catalogueSource !== undefined).length).toBe(starCount);
+ expect(initial.length).toBe(499); // Includes the bank root, markers, empty star wrapper and hidden nodes.
+ expect(initial.length - slabCount * 3 - starCount).toBe(46);
+ expect(predicted).toBe(CSS_COMPILER_RENDER_BUDGET.maximumElements);
+ expect(initial.length).toBeLessThanOrEqual(predicted);
+ expect(Number(root.dataset.volumeResidentDomNodes)).toBe(initial.length);
+ for (const id of ['second', 'third', 'first']) for (const distance of [4, 100]) {
+ runtime.selectLens(id);
+ for (const direction of [[0, 0, 1], [1, 0, 0], [0, 1, 0], [1, 1, 1], [1, 1, .4], [0, 0, -1]] as const) {
+ runtime.publish(publication(distance, direction));
+ runtime.setStarsVisible(true); runtime.setStarsVisible(false);
+ const current = descendants(root);
+ expect(current.length).toBeLessThanOrEqual(CSS_COMPILER_RENDER_BUDGET.maximumElements);
+ expect(current.every((node, index) => node === initial[index])).toBe(true);
+ expect(current.length).toBe(initial.length);
+ expect(root.dataset.volumeResidentTopologyCount).toBe('1');
+ }
+ }
+ expect(initial.some(node => node.style.display === 'none' || node.style.visibility === 'hidden' || node.hidden)).toBe(true);
+ runtime.destroy(); expect(f.host.children).toEqual([f.before]);
+});
+
test('catalogue points project prepared positions, cull hidden support and keep nodes when presentation changes', () => {
const f = dom(), initial = points();
const mount = mountPreparedCataloguePoints({ ...f.options, payload: initial });
@@ -142,13 +202,17 @@ test('a distinct topology is allocated only on first selection and then retained
const root = runtime.root as unknown as FakeElement;
expect(root.children.filter(node => node.className === 'prepared-volume-lens-cloud')).toHaveLength(1);
expect(root.dataset).toMatchObject({ volumeTopologyCount: '2', volumeResidentTopologyCount: '1' });
+ expect(descendants(root)).toHaveLength(29); // Three shared stars and one three-slab topology, all optical copies retained.
runtime.selectLens('second');
const families = root.children.filter(node => node.className === 'prepared-volume-lens-cloud');
expect(families).toHaveLength(2); expect(root.dataset.volumeResidentTopologyCount).toBe('2');
+ expect(descendants(root)).toHaveLength(52); // A second topology costs its wrappers and leaves even when hidden.
+ expect(root.dataset.volumeResidentDomNodes).toBe('52');
expect(families.filter(node => node.style.display !== 'none').map(node => node.dataset.volumeLens)).toEqual(['second']);
runtime.selectLens('third');
expect(root.children.filter(node => node.className === 'prepared-volume-lens-cloud')).toEqual(families);
expect(families.filter(node => node.style.display !== 'none').map(node => node.dataset.volumeLens)).toEqual(['third']);
+ expect(descendants(root)).toHaveLength(52);
runtime.destroy();
});
diff --git a/src/sources/baumgardt-vasiliev-2021.json b/src/sources/baumgardt-vasiliev-2021.json
new file mode 100644
index 0000000000..e956cf3157
--- /dev/null
+++ b/src/sources/baumgardt-vasiliev-2021.json
@@ -0,0 +1,52 @@
+{
+ "id": "baumgardt-vasiliev-2021",
+ "kind": "publication",
+ "identityLevel": "work",
+ "title": "Accurate distances to Galactic globular clusters through a combination of Gaia EDR3, HST and literature data",
+ "identifiers": [
+ {
+ "type": "DOI",
+ "value": "10.1093/mnras/stab1474"
+ }
+ ],
+ "links": [
+ {
+ "role": "landing",
+ "url": "https://doi.org/10.1093/mnras/stab1474",
+ "label": "Published paper"
+ },
+ {
+ "role": "original",
+ "url": "https://arxiv.org/pdf/2105.09526",
+ "label": "Inspected full-paper copy"
+ }
+ ],
+ "evidence": [
+ {
+ "url": "https://arxiv.org/pdf/2105.09526",
+ "checkedOn": "2026-09-20",
+ "locator": "Table 2, NGC 5139, combined distance 5.426 +/- 0.047 kpc; full paper downloaded and inspected"
+ }
+ ],
+ "creators": [
+ "Holger Baumgardt",
+ "Eugene Vasiliev"
+ ],
+ "publisher": "Monthly Notices of the Royal Astronomical Society",
+ "publicationDate": "2021",
+ "relations": [],
+ "statements": [
+ {
+ "kind": "credit",
+ "text": "Holger Baumgardt; Eugene Vasiliev",
+ "scope": "Published scientific method and measurements",
+ "evidence": "https://doi.org/10.1093/mnras/stab1474"
+ },
+ {
+ "kind": "limitation",
+ "text": "The adopted combined distance locates the cluster as a whole; it does not determine each image feature’s or star’s line-of-sight position.",
+ "scope": "Adopted interpretation",
+ "evidence": "https://doi.org/10.1093/mnras/stab1474"
+ }
+ ]
+}
diff --git a/src/sources/dsouza-rix-2013.json b/src/sources/dsouza-rix-2013.json
new file mode 100644
index 0000000000..c5c632c98d
--- /dev/null
+++ b/src/sources/dsouza-rix-2013.json
@@ -0,0 +1,52 @@
+{
+ "id": "dsouza-rix-2013",
+ "kind": "publication",
+ "identityLevel": "work",
+ "title": "Mass estimates from stellar proper motions: the mass of omega Centauri",
+ "identifiers": [
+ {
+ "type": "DOI",
+ "value": "10.1093/mnras/sts426"
+ }
+ ],
+ "links": [
+ {
+ "role": "landing",
+ "url": "https://doi.org/10.1093/mnras/sts426",
+ "label": "Published paper"
+ },
+ {
+ "role": "original",
+ "url": "https://wwwmpa.mpa-garching.mpg.de/mpa/publications/preprints/pp2013/MPA3537.pdf",
+ "label": "Inspected full-paper copy"
+ }
+ ],
+ "evidence": [
+ {
+ "url": "https://wwwmpa.mpa-garching.mpg.de/mpa/publications/preprints/pp2013/MPA3537.pdf",
+ "checkedOn": "2026-09-20",
+ "locator": "Table 1 and section 4.3: eight projected Gaussian components, angular widths, projected flattening and adopted inclination"
+ }
+ ],
+ "creators": [
+ "Richard D’Souza",
+ "Hans-Walter Rix"
+ ],
+ "publisher": "Monthly Notices of the Royal Astronomical Society",
+ "publicationDate": "2013",
+ "relations": [],
+ "statements": [
+ {
+ "kind": "credit",
+ "text": "Richard D’Souza; Hans-Walter Rix",
+ "scope": "Published scientific method and measurements",
+ "evidence": "https://doi.org/10.1093/mnras/sts426"
+ },
+ {
+ "kind": "limitation",
+ "text": "The table supplies a fitted projected stellar distribution. Oblate deprojection is conditional on inclination; coefficient errors/covariance are not given, and individual stellar depths are not measured.",
+ "scope": "Adopted interpretation",
+ "evidence": "https://doi.org/10.1093/mnras/sts426"
+ }
+ ]
+}
diff --git a/src/sources/eso-eso0844a.json b/src/sources/eso-eso0844a.json
new file mode 100644
index 0000000000..7b98d22a15
--- /dev/null
+++ b/src/sources/eso-eso0844a.json
@@ -0,0 +1,59 @@
+{
+ "id": "eso-eso0844a",
+ "kind": "data-product",
+ "identityLevel": "work",
+ "title": "Omega Centauri: MPG/ESO 2.2-m WFI optical composite",
+ "identifiers": [
+ {
+ "type": "ESO image",
+ "value": "eso0844a"
+ }
+ ],
+ "links": [
+ {
+ "role": "landing",
+ "url": "https://www.eso.org/public/images/eso0844a/",
+ "label": "Publisher image and instrument metadata"
+ },
+ {
+ "role": "original",
+ "url": "https://cdn.eso.org/images/original/eso0844a.tif",
+ "label": "Original publisher TIFF"
+ },
+ {
+ "role": "rights",
+ "url": "https://www.eso.org/public/outreach/copyright/",
+ "label": "ESO image use and full-credit terms"
+ }
+ ],
+ "evidence": [
+ {
+ "url": "https://www.eso.org/public/images/eso0844a/",
+ "checkedOn": "2026-09-20",
+ "locator": "About the image, coordinates, filters, credit and original download"
+ }
+ ],
+ "publisher": "European Southern Observatory",
+ "publicationDate": "2008-12-02",
+ "relations": [],
+ "statements": [
+ {
+ "kind": "credit",
+ "text": "ESO",
+ "scope": "Published image and derived views",
+ "evidence": "https://www.eso.org/public/images/eso0844a/"
+ },
+ {
+ "kind": "rights",
+ "text": "ESO public images use CC BY 4.0 unless otherwise noted; retain the complete visible credit and do not imply endorsement.",
+ "scope": "Selected published image",
+ "evidence": "https://www.eso.org/public/outreach/copyright/"
+ },
+ {
+ "kind": "limitation",
+ "text": "Published B/V/I display composite. The publisher identifies two mosaic gaps filled with lower-quality Digitized Sky Survey data; those areas do not supply independent WFI star detections.",
+ "scope": "Scientific interpretation",
+ "evidence": "https://www.eso.org/public/images/eso0844a/"
+ }
+ ]
+}
diff --git a/src/sources/eso-eso1119b.json b/src/sources/eso-eso1119b.json
new file mode 100644
index 0000000000..91c2134ec1
--- /dev/null
+++ b/src/sources/eso-eso1119b.json
@@ -0,0 +1,59 @@
+{
+ "id": "eso-eso1119b",
+ "kind": "data-product",
+ "identityLevel": "work",
+ "title": "Omega Centauri: VST/OmegaCAM optical composite",
+ "identifiers": [
+ {
+ "type": "ESO image",
+ "value": "eso1119b"
+ }
+ ],
+ "links": [
+ {
+ "role": "landing",
+ "url": "https://www.eso.org/public/images/eso1119b/",
+ "label": "Publisher image and instrument metadata"
+ },
+ {
+ "role": "original",
+ "url": "https://cdn.eso.org/images/original/eso1119b.tif",
+ "label": "Original publisher TIFF"
+ },
+ {
+ "role": "rights",
+ "url": "https://www.eso.org/public/outreach/copyright/",
+ "label": "ESO image use and full-credit terms"
+ }
+ ],
+ "evidence": [
+ {
+ "url": "https://www.eso.org/public/images/eso1119b/",
+ "checkedOn": "2026-09-20",
+ "locator": "About the image, coordinates, filters, credit and original download"
+ }
+ ],
+ "publisher": "European Southern Observatory",
+ "publicationDate": "2011-06-08",
+ "relations": [],
+ "statements": [
+ {
+ "kind": "credit",
+ "text": "ESO/INAF-VST/OmegaCAM. Acknowledgement: A. Grado, L. Limatola/INAF-Capodimonte Observatory",
+ "scope": "Published image and derived views",
+ "evidence": "https://www.eso.org/public/images/eso1119b/"
+ },
+ {
+ "kind": "rights",
+ "text": "ESO public images use CC BY 4.0 unless otherwise noted; retain the complete visible credit and do not imply endorsement.",
+ "scope": "Selected published image",
+ "evidence": "https://www.eso.org/public/outreach/copyright/"
+ },
+ {
+ "kind": "limitation",
+ "text": "Published G/R/I display composite. Sampling and display stretch are not calibrated stellar flux or measured depth.",
+ "scope": "Scientific interpretation",
+ "evidence": "https://www.eso.org/public/images/eso1119b/"
+ }
+ ]
+}
diff --git a/docs/telescope-family-examples/artifacts/F01.csv b/tests/fixtures/telescope-family-examples/artifacts/F01.csv
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F01.csv
rename to tests/fixtures/telescope-family-examples/artifacts/F01.csv
diff --git a/docs/telescope-family-examples/artifacts/F02.json b/tests/fixtures/telescope-family-examples/artifacts/F02.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F02.json
rename to tests/fixtures/telescope-family-examples/artifacts/F02.json
diff --git a/docs/telescope-family-examples/artifacts/F03.csv b/tests/fixtures/telescope-family-examples/artifacts/F03.csv
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F03.csv
rename to tests/fixtures/telescope-family-examples/artifacts/F03.csv
diff --git a/docs/telescope-family-examples/artifacts/F04.json b/tests/fixtures/telescope-family-examples/artifacts/F04.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F04.json
rename to tests/fixtures/telescope-family-examples/artifacts/F04.json
diff --git a/docs/telescope-family-examples/artifacts/F05.csv b/tests/fixtures/telescope-family-examples/artifacts/F05.csv
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F05.csv
rename to tests/fixtures/telescope-family-examples/artifacts/F05.csv
diff --git a/docs/telescope-family-examples/artifacts/F06.csv b/tests/fixtures/telescope-family-examples/artifacts/F06.csv
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F06.csv
rename to tests/fixtures/telescope-family-examples/artifacts/F06.csv
diff --git a/docs/telescope-family-examples/artifacts/F07.csv b/tests/fixtures/telescope-family-examples/artifacts/F07.csv
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F07.csv
rename to tests/fixtures/telescope-family-examples/artifacts/F07.csv
diff --git a/docs/telescope-family-examples/artifacts/F08.json b/tests/fixtures/telescope-family-examples/artifacts/F08.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F08.json
rename to tests/fixtures/telescope-family-examples/artifacts/F08.json
diff --git a/docs/telescope-family-examples/artifacts/F09.csv b/tests/fixtures/telescope-family-examples/artifacts/F09.csv
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F09.csv
rename to tests/fixtures/telescope-family-examples/artifacts/F09.csv
diff --git a/docs/telescope-family-examples/artifacts/F10.csv b/tests/fixtures/telescope-family-examples/artifacts/F10.csv
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F10.csv
rename to tests/fixtures/telescope-family-examples/artifacts/F10.csv
diff --git a/docs/telescope-family-examples/artifacts/F11.json b/tests/fixtures/telescope-family-examples/artifacts/F11.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F11.json
rename to tests/fixtures/telescope-family-examples/artifacts/F11.json
diff --git a/docs/telescope-family-examples/artifacts/F12.json b/tests/fixtures/telescope-family-examples/artifacts/F12.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F12.json
rename to tests/fixtures/telescope-family-examples/artifacts/F12.json
diff --git a/docs/telescope-family-examples/artifacts/F13.json b/tests/fixtures/telescope-family-examples/artifacts/F13.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F13.json
rename to tests/fixtures/telescope-family-examples/artifacts/F13.json
diff --git a/docs/telescope-family-examples/artifacts/F14.json b/tests/fixtures/telescope-family-examples/artifacts/F14.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F14.json
rename to tests/fixtures/telescope-family-examples/artifacts/F14.json
diff --git a/docs/telescope-family-examples/artifacts/F15.json b/tests/fixtures/telescope-family-examples/artifacts/F15.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F15.json
rename to tests/fixtures/telescope-family-examples/artifacts/F15.json
diff --git a/docs/telescope-family-examples/artifacts/F16.json b/tests/fixtures/telescope-family-examples/artifacts/F16.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F16.json
rename to tests/fixtures/telescope-family-examples/artifacts/F16.json
diff --git a/docs/telescope-family-examples/artifacts/F17.json b/tests/fixtures/telescope-family-examples/artifacts/F17.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F17.json
rename to tests/fixtures/telescope-family-examples/artifacts/F17.json
diff --git a/docs/telescope-family-examples/artifacts/F18.json b/tests/fixtures/telescope-family-examples/artifacts/F18.json
similarity index 100%
rename from docs/telescope-family-examples/artifacts/F18.json
rename to tests/fixtures/telescope-family-examples/artifacts/F18.json
diff --git a/docs/telescope-family-examples/descriptors/F02.json b/tests/fixtures/telescope-family-examples/descriptors/F02.json
similarity index 96%
rename from docs/telescope-family-examples/descriptors/F02.json
rename to tests/fixtures/telescope-family-examples/descriptors/F02.json
index a0057051e4..274930fd74 100644
--- a/docs/telescope-family-examples/descriptors/F02.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F02.json
@@ -28,7 +28,7 @@
"members": [
{
"id": "mixed",
- "path": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "path": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
"role": "science",
"bytes": 5760,
"sha256": "53c938b025d398986f37c8861a96caa1a988a403635d4aec1a06e3f4b4d92299",
diff --git a/docs/telescope-family-examples/descriptors/F03.json b/tests/fixtures/telescope-family-examples/descriptors/F03.json
similarity index 92%
rename from docs/telescope-family-examples/descriptors/F03.json
rename to tests/fixtures/telescope-family-examples/descriptors/F03.json
index bb86e50263..d6a137dde7 100644
--- a/docs/telescope-family-examples/descriptors/F03.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F03.json
@@ -5,7 +5,7 @@
"target": "mercury",
"acquisition": {
"kind": "repository",
- "identity": "../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json"
+ "identity": "../../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json"
},
"producingRecord": "src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "mascs",
- "path": "../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
+ "path": "../../../../src/objects/mercury/source/spectrum/mascs-global-area-weighted-mean.json",
"role": "science",
"bytes": 13609,
"sha256": "b1aaec4f4361318c40d4f4cbc10c798baca0dbfb309cdab41152d6f83925d756",
diff --git a/docs/telescope-family-examples/descriptors/F04.json b/tests/fixtures/telescope-family-examples/descriptors/F04.json
similarity index 94%
rename from docs/telescope-family-examples/descriptors/F04.json
rename to tests/fixtures/telescope-family-examples/descriptors/F04.json
index 292126d306..035fcada13 100644
--- a/docs/telescope-family-examples/descriptors/F04.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F04.json
@@ -5,7 +5,7 @@
"target": "europa",
"acquisition": {
"kind": "repository",
- "identity": "../../../tools/objects/hst/programs/europa-salt-map.scan.json"
+ "identity": "../../../../tools/objects/hst/programs/europa-salt-map.scan.json"
},
"producingRecord": "tools/objects/hst/programs/europa-salt-map.scan.reproduction.json",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "definition",
- "path": "../../../tools/objects/hst/programs/europa-salt-map.scan.json",
+ "path": "../../../../tools/objects/hst/programs/europa-salt-map.scan.json",
"role": "provenance",
"bytes": 31364,
"sha256": "08905793dabe7e5a81250e058c98ac5e7620d36bdaab9c0348147d3c87f79323",
@@ -37,7 +37,7 @@
},
{
"id": "receipt",
- "path": "../../../tools/objects/hst/programs/europa-salt-map.scan.reproduction.json",
+ "path": "../../../../tools/objects/hst/programs/europa-salt-map.scan.reproduction.json",
"role": "provenance",
"bytes": 21487,
"sha256": "c23af3b17dd98e3bc95e2620e4729390889a986383e0897eee2b8cf885f5f49e",
diff --git a/docs/telescope-family-examples/descriptors/F05.json b/tests/fixtures/telescope-family-examples/descriptors/F05.json
similarity index 95%
rename from docs/telescope-family-examples/descriptors/F05.json
rename to tests/fixtures/telescope-family-examples/descriptors/F05.json
index 4ef7567e23..8d639ea9b9 100644
--- a/docs/telescope-family-examples/descriptors/F05.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F05.json
@@ -5,7 +5,7 @@
"target": "haumea",
"acquisition": {
"kind": "repository",
- "identity": "../../../src/objects/haumea/source/photometry/disc-color.json"
+ "identity": "../../../../src/objects/haumea/source/photometry/disc-color.json"
},
"producingRecord": "src/objects/haumea/source/photometry/disc-color.json",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "colours",
- "path": "../../../src/objects/haumea/source/photometry/disc-color.json",
+ "path": "../../../../src/objects/haumea/source/photometry/disc-color.json",
"role": "science",
"bytes": 2084,
"sha256": "9537eff943560b6d733cdf0aa7b4d675d75e45a7e1b833cd7e56619f4331debc",
diff --git a/docs/telescope-family-examples/descriptors/F06.json b/tests/fixtures/telescope-family-examples/descriptors/F06.json
similarity index 92%
rename from docs/telescope-family-examples/descriptors/F06.json
rename to tests/fixtures/telescope-family-examples/descriptors/F06.json
index d6ad9173c5..978f8c6fde 100644
--- a/docs/telescope-family-examples/descriptors/F06.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F06.json
@@ -5,7 +5,7 @@
"target": "hd-189733",
"acquisition": {
"kind": "repository",
- "identity": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ "identity": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
},
"producingRecord": "src/objects/hd-189733/source/manifest.json",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "tess-41",
- "path": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "path": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
"role": "science",
"bytes": 1944000,
"sha256": "442c1d97c549019f46b98026a6b74cd380112722e3e230e79203559f67cc326b",
diff --git a/docs/telescope-family-examples/descriptors/F07.json b/tests/fixtures/telescope-family-examples/descriptors/F07.json
similarity index 96%
rename from docs/telescope-family-examples/descriptors/F07.json
rename to tests/fixtures/telescope-family-examples/descriptors/F07.json
index a394a59a7f..73c42121d2 100644
--- a/docs/telescope-family-examples/descriptors/F07.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F07.json
@@ -28,7 +28,7 @@
"members": [
{
"id": "cdf",
- "path": "../../../tests/fixtures/telescope-families/wind-rad1/wi_l2_wav_rad1_20200101_v01.cdf",
+ "path": "../../telescope-families/wind-rad1/wi_l2_wav_rad1_20200101_v01.cdf",
"role": "science",
"bytes": 423932,
"sha256": "a1fd0d0c2487592cd0a43672f50aede256dcdc2fed8bc4c3a87fb0799b20647b",
diff --git a/docs/telescope-family-examples/descriptors/F08.json b/tests/fixtures/telescope-family-examples/descriptors/F08.json
similarity index 96%
rename from docs/telescope-family-examples/descriptors/F08.json
rename to tests/fixtures/telescope-family-examples/descriptors/F08.json
index 36175ffa27..91d314fcbe 100644
--- a/docs/telescope-family-examples/descriptors/F08.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F08.json
@@ -5,7 +5,7 @@
"target": "hd-189733",
"acquisition": {
"kind": "repository",
- "identity": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
+ "identity": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits"
},
"producingRecord": "src/objects/hd-189733/source/manifest.json",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "fits",
- "path": "../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
+ "path": "../../../../src/objects/hd-189733/source/photometry/tess/tess2021204101404-s0041-0000000256364928-0212-s_lc.fits",
"role": "science",
"bytes": 1944000,
"sha256": "442c1d97c549019f46b98026a6b74cd380112722e3e230e79203559f67cc326b",
diff --git a/docs/telescope-family-examples/descriptors/F09.json b/tests/fixtures/telescope-family-examples/descriptors/F09.json
similarity index 96%
rename from docs/telescope-family-examples/descriptors/F09.json
rename to tests/fixtures/telescope-family-examples/descriptors/F09.json
index 1682f781ba..772eebef9f 100644
--- a/docs/telescope-family-examples/descriptors/F09.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F09.json
@@ -5,7 +5,7 @@
"target": "m1",
"acquisition": {
"kind": "repository",
- "identity": "../../../src/objects/m1/source/stellar-field.json"
+ "identity": "../../../../src/objects/m1/source/stellar-field.json"
},
"producingRecord": "src/objects/m1/source/stellar-field.json",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "gaia-field",
- "path": "../../../src/objects/m1/source/stellar-field.json",
+ "path": "../../../../src/objects/m1/source/stellar-field.json",
"role": "science",
"bytes": 128434,
"sha256": "4b4502d725e6a4df12d46a6aa0339556140a82d8f0dde2df8b32db840ca2d177",
diff --git a/docs/telescope-family-examples/descriptors/F10.json b/tests/fixtures/telescope-family-examples/descriptors/F10.json
similarity index 95%
rename from docs/telescope-family-examples/descriptors/F10.json
rename to tests/fixtures/telescope-family-examples/descriptors/F10.json
index 4bfe12428d..c819a467fb 100644
--- a/docs/telescope-family-examples/descriptors/F10.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F10.json
@@ -5,7 +5,7 @@
"target": "polaris",
"acquisition": {
"kind": "repository",
- "identity": "../../../tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz"
+ "identity": "../../telescope-families/chandra-events/acisf06431N003_evt2.fits.gz"
},
"producingRecord": "tools/objects/chandra/programs/polaris-vfaint.acisf06431N003_evt2.reproduction.json",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "events",
- "path": "../../../tests/fixtures/telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
+ "path": "../../telescope-families/chandra-events/acisf06431N003_evt2.fits.gz",
"role": "science",
"bytes": 2486220,
"sha256": "14d5d8da4fcf108a7ec6d3d5ee8db31ed7e641ba8f66a52f7671322b928ce0e5",
diff --git a/docs/telescope-family-examples/descriptors/F11.json b/tests/fixtures/telescope-family-examples/descriptors/F11.json
similarity index 91%
rename from docs/telescope-family-examples/descriptors/F11.json
rename to tests/fixtures/telescope-family-examples/descriptors/F11.json
index 93d3f9e69f..e37526f2d6 100644
--- a/docs/telescope-family-examples/descriptors/F11.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F11.json
@@ -5,7 +5,7 @@
"target": "J1008+0730",
"acquisition": {
"kind": "repository",
- "identity": "../../../tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits"
+ "identity": "../../telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits"
},
"producingRecord": "RASG datasets v0.0.5 BSD-2-Clause",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "uvfits",
- "path": "../../../tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
+ "path": "../../telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits",
"role": "science",
"bytes": 4253760,
"sha256": "926ed6175d6725f6f27b04a97b658d13341f528a3847b5bc1b7246a24bde4b27",
diff --git a/docs/telescope-family-examples/descriptors/F12.json b/tests/fixtures/telescope-family-examples/descriptors/F12.json
similarity index 94%
rename from docs/telescope-family-examples/descriptors/F12.json
rename to tests/fixtures/telescope-family-examples/descriptors/F12.json
index bcf720e94c..017fb50f99 100644
--- a/docs/telescope-family-examples/descriptors/F12.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F12.json
@@ -5,7 +5,7 @@
"target": "betelgeuse",
"acquisition": {
"kind": "repository",
- "identity": "../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits"
+ "identity": "../../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits"
},
"producingRecord": "src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
"sourceClassifications": [
@@ -29,7 +29,7 @@
"members": [
{
"id": "oifits",
- "path": "../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
+ "path": "../../../../src/objects/betelgeuse/source/observations/betelgeuse-matisse-2020-02-continuum.oifits",
"role": "science",
"bytes": 135360,
"sha256": "e4f41d2aba17736ff1fca5e819d60eaf3d38a8e784f6fc2998d587b0c682bcbd",
diff --git a/docs/telescope-family-examples/descriptors/F13.json b/tests/fixtures/telescope-family-examples/descriptors/F13.json
similarity index 97%
rename from docs/telescope-family-examples/descriptors/F13.json
rename to tests/fixtures/telescope-family-examples/descriptors/F13.json
index c93fa557e7..812695098d 100644
--- a/docs/telescope-family-examples/descriptors/F13.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F13.json
@@ -4,7 +4,7 @@
"id": "rm-lite-documented-full-stokes",
"acquisition": {
"kind": "archive",
- "identity": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits"
+ "identity": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits"
},
"producingRecord": "RM-lite public 3D RM-synthesis documentation; BSD-3-Clause source fixture.",
"sourceClassifications": [
@@ -28,7 +28,7 @@
"members": [
{
"id": "stokes",
- "path": "../../../tests/fixtures/telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
+ "path": "../../telescope-families/f13-stokes/rm-lite-documented-full-stokes.fits",
"role": "science",
"bytes": 5760,
"sha256": "53c938b025d398986f37c8861a96caa1a988a403635d4aec1a06e3f4b4d92299",
diff --git a/docs/telescope-family-examples/descriptors/F14.json b/tests/fixtures/telescope-family-examples/descriptors/F14.json
similarity index 96%
rename from docs/telescope-family-examples/descriptors/F14.json
rename to tests/fixtures/telescope-family-examples/descriptors/F14.json
index 30a07e619c..f3cb4de33f 100644
--- a/docs/telescope-family-examples/descriptors/F14.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F14.json
@@ -28,7 +28,7 @@
"members": [
{
"id": "map",
- "path": "../../../tests/fixtures/telescope-families/f14-healpix/bayestar.fits.gz",
+ "path": "../../telescope-families/f14-healpix/bayestar.fits.gz",
"role": "science",
"bytes": 102936,
"sha256": "18823330e933185c7bb8df402d1abbf20da7dffe34b2a7b94d171a961d224515",
diff --git a/docs/telescope-family-examples/descriptors/F15.json b/tests/fixtures/telescope-family-examples/descriptors/F15.json
similarity index 94%
rename from docs/telescope-family-examples/descriptors/F15.json
rename to tests/fixtures/telescope-family-examples/descriptors/F15.json
index 729657a187..c85e13869f 100644
--- a/docs/telescope-family-examples/descriptors/F15.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F15.json
@@ -29,7 +29,7 @@
"members": [
{
"id": "image",
- "path": "../../../tests/fixtures/telescope-families/geographos-radar/ge007.fit",
+ "path": "../../telescope-families/geographos-radar/ge007.fit",
"role": "science",
"bytes": 72000,
"sha256": "e26d04af718cd5f98a2ba0b1f3a9beee509f1ae2168ddec33cab24653a8725a2",
@@ -37,7 +37,7 @@
},
{
"id": "label",
- "path": "../../../tests/fixtures/telescope-families/geographos-radar/ge007.lbl",
+ "path": "../../telescope-families/geographos-radar/ge007.lbl",
"role": "label",
"bytes": 3600,
"sha256": "e505b5a1f8d9b38d64a55571d0fa14f14b572ad09cffda5d89b3355e732d2f0c",
diff --git a/tests/fixtures/telescope-family-examples/descriptors/F16.json b/tests/fixtures/telescope-family-examples/descriptors/F16.json
new file mode 100644
index 0000000000..0b85917e36
--- /dev/null
+++ b/tests/fixtures/telescope-family-examples/descriptors/F16.json
@@ -0,0 +1,88 @@
+{
+ "schema": "cssearth-telescope-product-descriptor@1",
+ "dataset": {
+ "id": "stellar-neighbourhood",
+ "target": "stellar-neighbourhood",
+ "acquisition": {
+ "kind": "repository",
+ "identity": "src/objects/stellar-neighbourhood/object.json"
+ },
+ "producingRecord": "src/objects/stellar-neighbourhood/object.json",
+ "sourceClassifications": [
+ {
+ "term": "physical point field",
+ "vocabulary": "cssearth-object@1",
+ "version": "1",
+ "status": "source"
+ }
+ ],
+ "families": [
+ "F16"
+ ],
+ "profiles": [
+ {
+ "handlerId": "f16-spatial-physical",
+ "profileId": "existing-physical-object@1"
+ }
+ ]
+ },
+ "members": [
+ {
+ "id": "object",
+ "path": "../../../../src/objects/stellar-neighbourhood/object.json",
+ "role": "science",
+ "bytes": 906,
+ "sha256": "8234b27bdc8ebe458af8dc29ea8dbbf4fdfabc925497c78068eeb9c421ddcd7e",
+ "mediaType": "application/json"
+ }
+ ],
+ "components": [
+ {
+ "id": "points",
+ "name": "stellar-neighbourhood points",
+ "families": [
+ "F16"
+ ],
+ "locations": [
+ {
+ "memberId": "object",
+ "path": "object.json"
+ }
+ ],
+ "representation": {
+ "kind": "physical-field",
+ "topology": "points",
+ "samples": null
+ },
+ "axes": [],
+ "columns": [],
+ "quantity": {
+ "name": "physical point samples",
+ "semantics": "Existing prepared physical object; no depth is inferred from an angular or spectral observation."
+ },
+ "calibration": {
+ "state": "model-derived",
+ "basis": [
+ "Validated by the existing points loader across 7 pinned inputs.",
+ "{\"source\":{\"schema\":\"cssearth-source-provenance@1\",\"sources\":[{\"id\":\"catalogue\",\"path\":\"stars-hyg.gxct\",\"sha256\":\"d1574537361ac78d67f32ddf30ac0122e9d52115851f1519cb91bed26dcf8e74\",\"bytes\":4818664,\"key\":\"catalogs/stars-hyg\",\"source\":\"HYG v4.4 (Hipparcos-Yale-Gliese Database, D. Nash / astronexus)\",\"license\":\"CC-BY-SA-4.0\",\"url\":\"https://codeberg.org/astronexus/hyg/raw/branch/main/data/hyg/CURRENT/LICENSE\",\"epoch\":\"ICRS/J2000.0 equinox and coordinate epoch; HYG has already propagated Hipparcos source positions to J2000.0.\",\"built\":\"2026-09-02\",\"extra\":{\"count\":109389,\"columns\":[\"posPc\",\"absMag\",\"teffK\",\"radiusSolar\",\"colorIndexBv\",\"hip\",\"name\",\"pmRaMasPerYr\",\"pmDecMasPerYr\"],\"droppedSun\":1,\"droppedNonPositiveDist\":0,\"droppedSentinelDist\":10224,\"totalSourceRows\":119614},\"credit\":\"HYG Stellar Database by David Nash / Astronexus. Prepared derivatives licensed CC-BY-SA-4.0.\",\"modifications\":\"Complete row set reordered into a spatial hierarchy; exact HIP matches adopt existing detailed body astrometry at the fixed scene epoch, preserving HYG apparent magnitude at the Sun. Other coordinates are unchanged. Luminosity-weighted proxies and 32-color display quantization.\"}],\"qualification\":\"Local generated assets; no network publication. HYG astrometry is a local stellar neighbourhood, not a full Milky Way stellar census.\",\"sourceBinding\":{\"kind\":\"catalogued\",\"references\":[{\"catalogueId\":\"hyg-v44\",\"role\":\"material\",\"evidence\":\"src/objects/stellar-neighbourhood/source/provenance.json@8d2f45b58b5ea9a0b69a81242c51aa6e6d6ebcdf#/sources/0\"}]},\"catalogueDisplay\":{\"feature\":\"stars\",\"label\":\"HYG\",\"credit\":\"Star catalogue · David Nash / Astronexus · CC BY-SA 4.0.\",\"description\":\"HYG Stellar Database by David Nash / Astronexus. Prepared derivatives licensed CC-BY-SA-4.0.\",\"footerAliases\":[\"https://codeberg.org/astronexus/hyg/raw/branch/main/data/hyg/CURRENT/LICENSE\"]}},\"catalogueMetadata\":{\"source\":\"HYG v4.4 (Hipparcos-Yale-Gliese Database, D. Nash / astronexus)\",\"epoch\":\"ICRS/J2000.0 equinox and coordinate epoch\",\"built\":\"2026-09-02\",\"sentinelDistPc\":100000,\"sentinelDistDoc\":\"dist == 100000.0 marks an unmeasurable/negative Hipparcos parallax; rows with it are dropped, not shipped.\",\"hipSentinel\":0,\"hipSentinelDoc\":\"hip == 0 means the row has no Hipparcos catalogue number.\",\"teffCalibration\":\"Ballesteros (2012) eq. 14, B-V -> Teff\",\"totalSourceRows\":119614,\"droppedSun\":1,\"droppedNonPositiveDist\":0,\"droppedSentinelDist\":10224,\"survivingRows\":109389},\"reconciliation\":{\"sourceEpochDescription\":\"ICRS/J2000 equinox; source astrometry primarily Hipparcos-2007 reduction, epoch J1991.25\",\"epochJdTt\":2461286.5,\"records\":[{\"bodyId\":\"polaris\",\"hipparcosId\":11767,\"sourceRow\":10818,\"originalPositionPc\":[1.3430999517440796,1.0476289987564087,132.6149139404297],\"positionPc\":[1.386060118675232,1.0821382999420166,136.8882598876953],\"originalAbsoluteMagnitude\":-3.6429998874664307,\"absoluteMagnitude\":-3.71186900138855,\"astrometry\":{\"hipparcosId\":11767,\"rightAscensionDegrees\":37.954560670189856,\"declinationDegrees\":89.26410896994187,\"positionEpochJulianYear\":2000,\"distanceParsecs\":136.9,\"properMotionRaMasPerYear\":44.48,\"properMotionDecMasPerYear\":-11.85,\"radialVelocityKmPerS\":-16.42,\"presentationUp\":\"display-axis\",\"sources\":{\"position\":\"SIMBAD basic record for * alf UMi (HD 8890, HIP 11767), ICRS J2000 position from the Hipparcos new reduction (van Leeuwen 2007, A&A 474, 653; bibcode 2007A&A...474..653V), retrieved 2026-09-16; cross-identification HIP 11767: https://simbad.cds.unistra.fr/simbad/sim-id?Ident=HIP%2011767\",\"distance\":\"136.90 +/- 0.34 pc, the distance Evans et al. (2024, ApJ 971, 190) adopt: the Gaia DR3 parallax of the wide companion Polaris B (7.2869 +/- 0.0178 mas via SIMBAD) with the Lindegren zero-point offset. Polaris Aa itself is too bright for a reliable Gaia parallax; Hipparcos gives 7.54 mas (133 pc).\",\"properMotion\":\"Hipparcos new reduction (van Leeuwen 2007) via SIMBAD: 44.48, -11.85 mas/yr\",\"radialVelocity\":\"-16.42 km/s, the systemic velocity of the spectroscopic binary in the SB9 catalogue (Pourbaix et al. 2004, A&A 424, 727) via SIMBAD\"}}},{\"bodyId\":\"r-doradus\",\"hipparcosId\":21479,\"sourceRow\":19852,\"originalPositionPc\":[9.086167335510254,23.906892776489258,-48.25687026977539],\"positionPc\":[9.086528778076172,23.906461715698242,-48.25751876831055],\"originalAbsoluteMagnitude\":1.902999997138977,\"absoluteMagnitude\":1.9029823541641235,\"astrometry\":{\"hipparcosId\":21479,\"rightAscensionDegrees\":69.18996363611527,\"declinationDegrees\":-62.077165937032035,\"positionEpochJulianYear\":2000,\"distanceParsecs\":54.61496450027307,\"properMotionRaMasPerYear\":-69.36,\"properMotionDecMasPerYear\":-75.78,\"radialVelocityKmPerS\":26.1,\"presentationUp\":\"display-axis\",\"sources\":{\"position\":\"SIMBAD basic record for V* R Dor (HD 29712, HIP 21479), ICRS J2000 position from the revised Hipparcos reduction (van Leeuwen 2007, A&A 474, 653; bibcode 2007A&A...474..653V), retrieved 2026-09-17; cross-identification HIP 21479: https://simbad.cds.unistra.fr/simbad/sim-id?Ident=HIP%2021479\",\"distance\":\"Hipparcos parallax 18.31 +/- 0.99 mas (van Leeuwen 2007) via SIMBAD: 54.615 pc. Vlemmings et al. (2024, Nature 633, 323) adopt the same measurement as 55 +/- 3 pc and state that there is no useable Gaia parallax for this star.\",\"properMotion\":\"van Leeuwen (2007) via SIMBAD: -69.36, -75.78 mas/yr. The ALMA image confirms it: the star sits 1.63 arcsec west and 1.78 arcsec south of this J2000 position, which is 23.5 years of this proper motion.\",\"radialVelocity\":\"General Catalogue of Stellar Radial Velocities (Wilson 1953, bibcode 1953GCRV..C......0W) via SIMBAD: 26.1 +/- 2.0 km/s.\"}}},{\"bodyId\":\"ce-tauri\",\"hipparcosId\":25945,\"sourceRow\":23960,\"originalPositionPc\":[62.98653030395508,516.946533203125,175.20001220703125],\"positionPc\":[75.38333129882812,618.6912231445312,209.68223571777344],\"originalAbsoluteMagnitude\":-4.380000114440918,\"absoluteMagnitude\":-4.770141124725342,\"astrometry\":{\"hipparcosId\":25945,\"rightAscensionDegrees\":83.05313179526708,\"declinationDegrees\":18.59423660180722,\"positionEpochJulianYear\":2000,\"distanceParsecs\":657.5919969093286,\"properMotionRaMasPerYear\":0.957,\"properMotionDecMasPerYear\":-4.511,\"radialVelocityKmPerS\":23.75,\"presentationUp\":\"display-axis\",\"sources\":{\"position\":\"SIMBAD basic record for * 119 Tau (CE Tau, HD 36389, HIP 25945), ICRS J2000 position from Gaia EDR3/DR3 (Gaia Collaboration 2020, yCat I/350; bibcode 2020yCat.1350....0G), retrieved 2026-09-16; cross-identification HIP 25945: https://simbad.cds.unistra.fr/simbad/sim-id?Ident=HIP%2025945\",\"distance\":\"Gaia DR3 parallax 1.5207 +/- 0.2661 mas via SIMBAD: 658 pc. Hipparcos (van Leeuwen 2007) gives 1.82 +/- 0.26 mas, the 549 pc Montargès et al. (2018) adopt.\",\"properMotion\":\"Gaia DR3 via SIMBAD: 0.957, -4.511 mas/yr\",\"radialVelocity\":\"Famaey et al. (2005, A&A 430, 165; bibcode 2005A&A...430..165F) via SIMBAD: 23.75 +/- 0.44 km/s\"}}},{\"bodyId\":\"betelgeuse\",\"hipparcosId\":27989,\"sourceRow\":25822,\"originalPositionPc\":[3.189296007156372,151.3643798828125,19.68214225769043],\"positionPc\":[3.5089073181152344,166.5616912841797,21.65852165222168],\"originalAbsoluteMagnitude\":-5.468999862670898,\"absoluteMagnitude\":-5.676757335662842,\"astrometry\":{\"hipparcosId\":27989,\"rightAscensionDegrees\":88.79293899077537,\"declinationDegrees\":7.407063995272694,\"positionEpochJulianYear\":2000,\"distanceParsecs\":168,\"properMotionRaMasPerYear\":27.54,\"properMotionDecMasPerYear\":11.3,\"radialVelocityKmPerS\":21.91,\"presentationUp\":\"display-axis\",\"sources\":{\"position\":\"SIMBAD basic record for * alf Ori, ICRS J2000 position from van Leeuwen (2007, A&A 474, 653; bibcode 2007A&A...474..653V), retrieved 2026-09-16; cross-identification HIP 27989: https://simbad.cds.unistra.fr/simbad/sim-id?Ident=HIP%2027989\",\"distance\":\"Joyce et al. (2020, ApJ 902, 63; arXiv:2006.09837): 168 (+27/-15) pc, parallax 5.95 (+0.58/-0.85) mas. Hipparcos (van Leeuwen 2007) gives 6.55 +/- 0.83 mas (153 pc); Harper et al. (2017, AJ 154, 11) give 4.51 +/- 0.80 mas (222 pc).\",\"properMotion\":\"van Leeuwen (2007, A&A 474, 653) via SIMBAD: 27.54, 11.30 mas/yr, error 1.03 mas/yr\",\"radialVelocity\":\"Famaey et al. (2005, A&A 430, 165; bibcode 2005A&A...430..165F) via SIMBAD: 21.91 +/- 0.51 km/s\"}}},{\"bodyId\":\"antares\",\"hipparcosId\":80763,\"sourceRow\":73646,\"originalPositionPc\":[-58.5426025390625,-140.30758666992188,-75.57476806640625],\"positionPc\":[-58.54274368286133,-140.3072052001953,-75.57518768310547],\"originalAbsoluteMagnitude\":-5.089000225067139,\"absoluteMagnitude\":-5.088999271392822,\"astrometry\":{\"hipparcosId\":80763,\"rightAscensionDegrees\":247.3519154198264,\"declinationDegrees\":-26.432002611950832,\"positionEpochJulianYear\":2000,\"distanceParsecs\":169.77928692699493,\"properMotionRaMasPerYear\":-12.11,\"properMotionDecMasPerYear\":-23.3,\"radialVelocityKmPerS\":-3.5,\"presentationUp\":\"display-axis\",\"sources\":{\"position\":\"SIMBAD basic record for * alf Sco (HD 148478, HIP 80763), ICRS J2000 position from the Hipparcos re-reduction (van Leeuwen 2007, A&A 474, 653; bibcode 2007A&A...474..653V), retrieved 2026-09-16; cross-identification HIP 80763: https://simbad.cds.unistra.fr/simbad/sim-id?Ident=HIP%2080763\",\"distance\":\"Hipparcos parallax 5.89 +/- 1.00 mas (van Leeuwen 2007) via SIMBAD: 170 pc. The star is too bright for a Gaia parallax.\",\"properMotion\":\"van Leeuwen (2007) via SIMBAD: -12.11, -23.30 mas/yr\",\"radialVelocity\":\"Gontcharov (2006, AstL 32, 759; bibcode 2006AstL...32..759G) via SIMBAD: -3.5 +/- 0.8 km/s\"}}},{\"bodyId\":\"hd-181327\",\"hipparcosId\":95270,\"sourceRow\":86757,\"originalPositionPc\":[10.647910118103027,-28.111202239990234,-42.202144622802734],\"positionPc\":[9.818360328674316,-25.920703887939453,-38.91460037231445],\"originalAbsoluteMagnitude\":3.4679999351501465,\"absoluteMagnitude\":3.6441261768341064,\"astrometry\":{\"hipparcosId\":95270,\"rightAscensionDegrees\":290.745785785032,\"declinationDegrees\":-54.538414066989446,\"positionEpochJulianYear\":2016,\"distanceParsecs\":47.776868212690886,\"properMotionRaMasPerYear\":24.402797199941578,\"properMotionDecMasPerYear\":-82.18575722363535,\"radialVelocityKmPerS\":0.07390876,\"presentationUp\":\"display-axis\",\"sources\":{\"position\":\"Gaia DR3 source 6643589352010758400 (Gaia Collaboration 2023, A&A 674, A1), ICRS at epoch J2016.0, from the archived row src/objects/hd-181327/source/photometry/gaia-dr3-source.csv; cross-identification HIP 95270: https://simbad.cds.unistra.fr/simbad/sim-id?Ident=HIP%2095270\",\"distance\":\"Gaia DR3 parallax 20.931 +/- 0.029 mas (same row): 1000/20.930631 = 47.777 pc, no zero-point correction\",\"properMotion\":\"Gaia DR3 (same row): 24.403, -82.186 mas/yr\",\"radialVelocity\":\"Gaia DR3 (same row): 0.07 +/- 0.13 km/s\"}}},{\"bodyId\":\"hd-189733\",\"hipparcosId\":98505,\"sourceRow\":89652,\"originalPositionPc\":[9.021016120910645,-15.51077938079834,7.509850025177002],\"positionPc\":[9.17152214050293,-15.769575119018555,7.634456157684326],\"originalAbsoluteMagnitude\":6.224999904632568,\"absoluteMagnitude\":6.189097881317139,\"astrometry\":{\"hipparcosId\":98505,\"rightAscensionDegrees\":300.1821218062407,\"declinationDegrees\":22.709741105168273,\"positionEpochJulianYear\":2016,\"distanceParsecs\":19.775801965002984,\"properMotionRaMasPerYear\":-3.208339784864691,\"properMotionDecMasPerYear\":-250.32333085817578,\"radialVelocityKmPerS\":-2.531722,\"presentationUp\":\"display-axis\",\"sources\":{\"position\":\"Gaia DR3 source 1827242816201846144 (Gaia Collaboration 2023, A&A 674, A1), ICRS at epoch J2016.0, from the archived row src/objects/hd-189733/source/photometry/gaia-dr3-source.csv; cross-identification HIP 98505: https://simbad.cds.unistra.fr/simbad/sim-id?Ident=HIP%2098505\",\"distance\":\"Gaia DR3 parallax 50.567 +/- 0.016 mas (same row): 1000/50.567 = 19.776 pc, no zero-point correction\",\"properMotion\":\"Gaia DR3 (same row): -3.208, -250.323 mas/yr\",\"radialVelocity\":\"Gaia DR3 (same row): -2.53 +/- 0.12 km/s\"}}},{\"bodyId\":\"pi1-gruis\",\"hipparcosId\":110478,\"sourceRow\":100558,\"originalPositionPc\":[103.3653335571289,-46.70558166503906,-117.24427795410156],\"positionPc\":[102.43231964111328,-46.283260345458984,-116.18620300292969],\"originalAbsoluteMagnitude\":0.3569999933242798,\"absoluteMagnitude\":0.3766902685165405,\"astrometry\":{\"hipparcosId\":110478,\"rightAscensionDegrees\":335.68420162374997,\"declinationDegrees\":-45.94799754471,\"positionEpochJulianYear\":2000,\"distanceParsecs\":161.65938025034984,\"properMotionRaMasPerYear\":31.106,\"properMotionDecMasPerYear\":-10.338,\"radialVelocityKmPerS\":0,\"presentationUp\":\"display-axis\",\"sources\":{\"position\":\"SIMBAD basic record for * pi.01 Gru (HD 212087, HIP 110478), ICRS J2000 position from Gaia EDR3/DR3 (Gaia Collaboration 2020, yCat I/350; bibcode 2020yCat.1350....0G), retrieved 2026-09-16; cross-identification HIP 110478: https://simbad.cds.unistra.fr/simbad/sim-id?Ident=HIP%20110478\",\"distance\":\"Gaia DR3 parallax 6.1858 +/- 0.4466 mas (RUWE 2.9; the star is a wide binary) via SIMBAD: 161.7 pc. Hipparcos (van Leeuwen 2007, A&A 474, 653) gives 6.13 +/- 0.76 mas, the 163 pc Paladini et al. (2018, Nature 553, 310) adopt.\",\"properMotion\":\"Gaia DR3 via SIMBAD: 31.106, -10.338 mas/yr\",\"radialVelocity\":\"No catalogued radial velocity: SIMBAD lists none, and the Pulkovo (III/252), GCRV (III/21) and Kharchenko (III/254) compilations have no entry for HIP 110478 (checked 2026-09-16). Zero is assumed; a 30 km/s error moves the scene position by one part in a million of the distance over the 26 years since J2000.\"}}}],\"policy\":\"Exact HIP identity join to detailed body astrometry; float32 parsec positions; preserve HYG apparent magnitude at the Sun. Unmatched rows remain at their catalogue epoch. The retained GXCT epoch description is legacy: HYG coordinates are J2000.0, not J1991.25.\"},\"qualification\":\"All catalogue rows retained; explicitly cross-identified detailed stars use body astrometry at the navigation epoch, with HYG apparent brightness preserved. Remaining rows retain HYG J2000.0 positions. Internal nodes approximate unresolved luminosity and position; no fixed pixel-error guarantee when the point pool is saturated.\"}"
+ ]
+ },
+ "flags": [],
+ "frame": {
+ "kind": "physical",
+ "name": "sun-icrf",
+ "epoch": "JD_TT 2461286.5"
+ },
+ "dependencyIds": []
+ }
+ ],
+ "dependencies": [],
+ "issues": [
+ {
+ "scope": "component",
+ "identity": "points",
+ "state": "unknown",
+ "reason": "Portable export retains prepared resources and source recipe; raw source datasets are referenced rather than bundled. Bounds: {\"min\":[-1024,-1024,-1024],\"max\":[1024,1024,1024]}."
+ }
+ ]
+}
diff --git a/docs/telescope-family-examples/descriptors/F17.json b/tests/fixtures/telescope-family-examples/descriptors/F17.json
similarity index 91%
rename from docs/telescope-family-examples/descriptors/F17.json
rename to tests/fixtures/telescope-family-examples/descriptors/F17.json
index cbe7e4edbf..73729fc997 100644
--- a/docs/telescope-family-examples/descriptors/F17.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F17.json
@@ -29,7 +29,7 @@
"members": [
{
"id": "raw",
- "path": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p.fit",
+ "path": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p.fit",
"role": "science",
"bytes": 273600,
"sha256": "c0c16f2c972f1a2b0b12acc2a22244a5ba054cf50768fb1bade203d62d04a29c",
@@ -37,7 +37,7 @@
},
{
"id": "calibrated",
- "path": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.fit",
+ "path": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.fit",
"role": "science",
"bytes": 535680,
"sha256": "c852aeb4858a579821e724419c4cdebfb895749423c039e54e75b72986a8de8d",
@@ -45,7 +45,7 @@
},
{
"id": "label",
- "path": "../../../tests/fixtures/telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.xml",
+ "path": "../../telescope-families/f17-mathilde-near-msi/m0042826360f0_2p_iof.xml",
"role": "label",
"bytes": 15271,
"sha256": "399ced8980cad90e87bb692aedeac32759d8a1ebea5c9ba4ece579d7d467d52d",
diff --git a/docs/telescope-family-examples/descriptors/F18.json b/tests/fixtures/telescope-family-examples/descriptors/F18.json
similarity index 89%
rename from docs/telescope-family-examples/descriptors/F18.json
rename to tests/fixtures/telescope-family-examples/descriptors/F18.json
index b918ce1207..9cc81f3639 100644
--- a/docs/telescope-family-examples/descriptors/F18.json
+++ b/tests/fixtures/telescope-family-examples/descriptors/F18.json
@@ -29,7 +29,7 @@
"members": [
{
"id": "archive",
- "path": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
+ "path": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
"role": "provenance",
"bytes": 3487,
"sha256": "2fd4446342943b72ec90e839f154e02e22841075ec21ce51ac4199fa78a5b0be",
@@ -37,7 +37,7 @@
},
{
"id": "product",
- "path": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "path": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
"role": "science",
"bytes": 4820,
"sha256": "ab824d182a6eeb08ac4b69ad1dd90c51f12070f3c5f66579258b645f72df4c47",
@@ -47,7 +47,7 @@
"components": [
{
"id": "member-archive",
- "name": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
+ "name": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.json",
"families": [
"F18"
],
@@ -77,7 +77,7 @@
},
{
"id": "member-product",
- "name": "../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
+ "name": "../../../../tools/objects/pds/programs/charon-pds-nh_charon_color_mosaic.archive-final.product.json",
"families": [
"F18"
],
diff --git a/docs/telescope-family-examples/manifest.json b/tests/fixtures/telescope-family-examples/manifest.json
similarity index 87%
rename from docs/telescope-family-examples/manifest.json
rename to tests/fixtures/telescope-family-examples/manifest.json
index 87ca1ad131..c2e0d201ea 100644
--- a/docs/telescope-family-examples/manifest.json
+++ b/tests/fixtures/telescope-family-examples/manifest.json
@@ -43,7 +43,7 @@
"bytes": 5760,
"sha256": "53c938b025d398986f37c8861a96caa1a988a403635d4aec1a06e3f4b4d92299"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F02.json nd-spectrum --params docs/telescope-family-examples/params/F02.json --out example-output/F02",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F02.json nd-spectrum --params tests/fixtures/telescope-family-examples/params/F02.json --out example-output/F02",
"artifact": {
"path": "artifacts/F02.json",
"bytes": 2462,
@@ -70,7 +70,7 @@
"bytes": 13609,
"sha256": "b1aaec4f4361318c40d4f4cbc10c798baca0dbfb309cdab41152d6f83925d756"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F03.json spectrum-export --out example-output/F03",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F03.json spectrum-export --out example-output/F03",
"artifact": {
"path": "artifacts/F03.csv",
"bytes": 10007,
@@ -97,7 +97,7 @@
"bytes": 21487,
"sha256": "c23af3b17dd98e3bc95e2620e4729390889a986383e0897eee2b8cf885f5f49e"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F04.json slit-scan-inspect --out example-output/F04",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F04.json slit-scan-inspect --out example-output/F04",
"artifact": {
"path": "artifacts/F04.json",
"bytes": 2739,
@@ -124,7 +124,7 @@
"bytes": 2084,
"sha256": "9537eff943560b6d733cdf0aa7b4d675d75e45a7e1b833cd7e56619f4331debc"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F05.json photometry-export --out example-output/F05",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F05.json photometry-export --out example-output/F05",
"artifact": {
"path": "artifacts/F05.csv",
"bytes": 151,
@@ -151,7 +151,7 @@
"bytes": 1944000,
"sha256": "442c1d97c549019f46b98026a6b74cd380112722e3e230e79203559f67cc326b"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F06.json time-series-select --params docs/telescope-family-examples/params/F06.json --out example-output/F06",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F06.json time-series-select --params tests/fixtures/telescope-family-examples/params/F06.json --out example-output/F06",
"artifact": {
"path": "artifacts/F06.csv",
"bytes": 343,
@@ -178,7 +178,7 @@
"bytes": 423932,
"sha256": "a1fd0d0c2487592cd0a43672f50aede256dcdc2fed8bc4c3a87fb0799b20647b"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F07.json dynamic-spectrum-export --params docs/telescope-family-examples/params/F07.json --out example-output/F07",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F07.json dynamic-spectrum-export --params tests/fixtures/telescope-family-examples/params/F07.json --out example-output/F07",
"artifact": {
"path": "artifacts/F07.csv",
"bytes": 354,
@@ -205,7 +205,7 @@
"bytes": 1944000,
"sha256": "442c1d97c549019f46b98026a6b74cd380112722e3e230e79203559f67cc326b"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F08.json table-inspect --out example-output/F08",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F08.json table-inspect --out example-output/F08",
"artifact": {
"path": "artifacts/F08.json",
"bytes": 3227,
@@ -232,7 +232,7 @@
"bytes": 128434,
"sha256": "4b4502d725e6a4df12d46a6aa0339556140a82d8f0dde2df8b32db840ca2d177"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F09.json astrometry-export --out example-output/F09",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F09.json astrometry-export --out example-output/F09",
"artifact": {
"path": "artifacts/F09.csv",
"bytes": 30677,
@@ -259,7 +259,7 @@
"bytes": 2486220,
"sha256": "14d5d8da4fcf108a7ec6d3d5ee8db31ed7e641ba8f66a52f7671322b928ce0e5"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F10.json selected-events --params docs/telescope-family-examples/params/F10.json --out example-output/F10",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F10.json selected-events --params tests/fixtures/telescope-family-examples/params/F10.json --out example-output/F10",
"artifact": {
"path": "artifacts/F10.csv",
"bytes": 1234502,
@@ -289,7 +289,7 @@
"bytes": 4253760,
"sha256": "926ed6175d6725f6f27b04a97b658d13341f528a3847b5bc1b7246a24bde4b27"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F11.json uvfits-visibility-export --params docs/telescope-family-examples/params/F11.json --out example-output/F11",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F11.json uvfits-visibility-export --params tests/fixtures/telescope-family-examples/params/F11.json --out example-output/F11",
"artifact": {
"path": "artifacts/F11.json",
"bytes": 1816,
@@ -316,7 +316,7 @@
"bytes": 135360,
"sha256": "e4f41d2aba17736ff1fca5e819d60eaf3d38a8e784f6fc2998d587b0c682bcbd"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F12.json v2-diagnostics --out example-output/F12",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F12.json v2-diagnostics --out example-output/F12",
"artifact": {
"path": "artifacts/F12.json",
"bytes": 153585,
@@ -346,7 +346,7 @@
"bytes": 5760,
"sha256": "53c938b025d398986f37c8861a96caa1a988a403635d4aec1a06e3f4b4d92299"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F13.json stokes-spectrum --params docs/telescope-family-examples/params/F13.json --out example-output/F13",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F13.json stokes-spectrum --params tests/fixtures/telescope-family-examples/params/F13.json --out example-output/F13",
"artifact": {
"path": "artifacts/F13.json",
"bytes": 537,
@@ -375,7 +375,7 @@
"bytes": 102936,
"sha256": "18823330e933185c7bb8df402d1abbf20da7dffe34b2a7b94d171a961d224515"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F14.json healpix-select --params docs/telescope-family-examples/params/F14.json --out example-output/F14",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F14.json healpix-select --params tests/fixtures/telescope-family-examples/params/F14.json --out example-output/F14",
"artifact": {
"path": "artifacts/F14.json",
"bytes": 528,
@@ -402,7 +402,7 @@
"bytes": 72000,
"sha256": "e26d04af718cd5f98a2ba0b1f3a9beee509f1ae2168ddec33cab24653a8725a2"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F15.json radar-profile --params docs/telescope-family-examples/params/F15.json --out example-output/F15",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F15.json radar-profile --params tests/fixtures/telescope-family-examples/params/F15.json --out example-output/F15",
"artifact": {
"path": "artifacts/F15.json",
"bytes": 235,
@@ -427,9 +427,9 @@
"path": "src/objects/stellar-neighbourhood/object.json",
"url": "https://codeberg.org/astronexus/hyg",
"bytes": 906,
- "sha256": "fa4dbdb551af2ecfa85dc4964218f8708bbab60401f8d452a7a5230776c376e1"
+ "sha256": "8234b27bdc8ebe458af8dc29ea8dbbf4fdfabc925497c78068eeb9c421ddcd7e"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F16.json spatial-inspect --out example-output/F16",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F16.json spatial-inspect --out example-output/F16",
"artifact": {
"path": "artifacts/F16.json",
"bytes": 399,
@@ -459,7 +459,7 @@
"bytes": 535680,
"sha256": "c852aeb4858a579821e724419c4cdebfb895749423c039e54e75b72986a8de8d"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F17.json near-msi-inspect --out example-output/F17",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F17.json near-msi-inspect --out example-output/F17",
"artifact": {
"path": "artifacts/F17.json",
"bytes": 850,
@@ -486,7 +486,7 @@
"bytes": 4820,
"sha256": "ab824d182a6eeb08ac4b69ad1dd90c51f12070f3c5f66579258b645f72df4c47"
},
- "command": "telescope family-run docs/telescope-family-examples/descriptors/F18.json enumerate-members --out example-output/F18",
+ "command": "telescope family-run tests/fixtures/telescope-family-examples/descriptors/F18.json enumerate-members --out example-output/F18",
"artifact": {
"path": "artifacts/F18.json",
"bytes": 1150,
diff --git a/docs/telescope-family-examples/params/F02.json b/tests/fixtures/telescope-family-examples/params/F02.json
similarity index 100%
rename from docs/telescope-family-examples/params/F02.json
rename to tests/fixtures/telescope-family-examples/params/F02.json
diff --git a/docs/telescope-family-examples/params/F06.json b/tests/fixtures/telescope-family-examples/params/F06.json
similarity index 100%
rename from docs/telescope-family-examples/params/F06.json
rename to tests/fixtures/telescope-family-examples/params/F06.json
diff --git a/docs/telescope-family-examples/params/F07.json b/tests/fixtures/telescope-family-examples/params/F07.json
similarity index 100%
rename from docs/telescope-family-examples/params/F07.json
rename to tests/fixtures/telescope-family-examples/params/F07.json
diff --git a/docs/telescope-family-examples/params/F10.json b/tests/fixtures/telescope-family-examples/params/F10.json
similarity index 100%
rename from docs/telescope-family-examples/params/F10.json
rename to tests/fixtures/telescope-family-examples/params/F10.json
diff --git a/docs/telescope-family-examples/params/F11.json b/tests/fixtures/telescope-family-examples/params/F11.json
similarity index 100%
rename from docs/telescope-family-examples/params/F11.json
rename to tests/fixtures/telescope-family-examples/params/F11.json
diff --git a/docs/telescope-family-examples/params/F13.json b/tests/fixtures/telescope-family-examples/params/F13.json
similarity index 100%
rename from docs/telescope-family-examples/params/F13.json
rename to tests/fixtures/telescope-family-examples/params/F13.json
diff --git a/docs/telescope-family-examples/params/F14.json b/tests/fixtures/telescope-family-examples/params/F14.json
similarity index 100%
rename from docs/telescope-family-examples/params/F14.json
rename to tests/fixtures/telescope-family-examples/params/F14.json
diff --git a/docs/telescope-family-examples/params/F15.json b/tests/fixtures/telescope-family-examples/params/F15.json
similarity index 100%
rename from docs/telescope-family-examples/params/F15.json
rename to tests/fixtures/telescope-family-examples/params/F15.json
diff --git a/tests/objects/browser/omega-centauri/native-focus-browser.mts b/tests/objects/browser/omega-centauri/native-focus-browser.mts
new file mode 100644
index 0000000000..8672d98227
--- /dev/null
+++ b/tests/objects/browser/omega-centauri/native-focus-browser.mts
@@ -0,0 +1,169 @@
+/** Native arrival, ordinary reselection and wheel zoom: never apply an inspection camera. */
+import assert from 'node:assert/strict';
+import { createHash } from 'node:crypto';
+import { readFile, mkdir, writeFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { chromium } from 'playwright';
+import { parsePreparedNebulaCatalog } from '@cssearth/catalog';
+import { parsePreparedWorldContext } from '../../../../src/renderers/css/dist/index.js';
+import { validatePreparedVolumeLenses } from '../../../../src/renderers/css/dist/universe.js';
+import { requireRecord } from '../../../../tools/source-values.mts';
+import { createTestPage } from '../../../../site/test/browser-observations.mts';
+
+const base = process.argv[2] ?? 'http://127.0.0.1:4210';
+const out = resolve(process.argv[3] ?? 'output/omega-native-focus');
+await mkdir(out, { recursive: true });
+const pins: { path: string; bytes: number; sha256: string }[] = [];
+async function input(path: string): Promise {
+ const bytes = await readFile(path);
+ pins.push({ path, bytes: bytes.length, sha256: createHash('sha256').update(bytes).digest('hex') });
+ return JSON.parse(bytes.toString('utf8'));
+}
+const context = parsePreparedWorldContext(await input('src/objects/sun/prepared/world-context.json'));
+const catalog = parsePreparedNebulaCatalog(await input('src/objects/omega-centauri/source/nebula.json'));
+const target = catalog.objects.find(object => object.id === 'omega-centauri');
+assert.ok(target);
+const payload = validatePreparedVolumeLenses(requireRecord(await input('src/objects/omega-centauri/prepared/lenses.json')).data);
+const radiusM = payload.framingRadiusUnits * payload.lenses[0]!.volume.frame.metersPerUnit;
+const viewport = { width: 1440, height: 1000 };
+const browser = await chromium.launch({ headless: true });
+const page = await createTestPage(browser, { viewport, deviceScaleFactor: 1, reducedMotion: 'reduce' });
+const errors: string[] = [], failed: string[] = [];
+page.on('pageerror', error => errors.push(error.message));
+page.on('response', response => { if (response.status() >= 400) failed.push(`${response.status()} ${response.url()}`); });
+const parameters = { frame: context.frame, target: target.positionM, radiusM };
+const measure = () => page.evaluate(({ frame, target, radiusM }) => {
+ const world = window.__cssearthTest.object('sun').camera.captureWorldCamera(frame);
+ const camera = window.__cssearthTest.physicalCamera('sun');
+ const distanceM = Math.hypot(...world.pose.positionM.map((value, axis) => value - target[axis]!));
+ return { world, distanceM, distanceRadii: distanceM / radiusM, projectedRadiusPixels: camera.focal * radiusM / distanceM };
+}, parameters);
+const settle = () => page.evaluate(() => new Promise(done => {
+ let remaining = 20;
+ const tick = () => --remaining > 0 ? requestAnimationFrame(tick) : done();
+ requestAnimationFrame(tick);
+}));
+const contextVisibility = () => page.evaluate(() => ({
+ volumes: [...document.querySelectorAll('[data-volume-lens-object]')].map(root => ({
+ id: root.dataset.volumeLensObject, display: getComputedStyle(root).display, opacity: Number(getComputedStyle(root).opacity),
+ })),
+ overview: [...document.querySelectorAll('[data-volume-opacity]')].map(root => ({
+ display: getComputedStyle(root).display, opacity: Number(getComputedStyle(root).opacity),
+ })),
+ stellar: [...document.querySelectorAll('.stellar-direct-points')].map(root => ({
+ display: getComputedStyle(root).display, opacity: Number(getComputedStyle(root).opacity),
+ })),
+}));
+const assertCloseContext = async () => {
+ const state = await contextVisibility();
+ assert.ok(state.volumes.some(bank => bank.id === 'omega-centauri' && bank.display !== 'none' && bank.opacity > 0));
+ assert.ok(state.volumes.filter(bank => bank.id !== 'omega-centauri').every(bank => bank.display === 'none'),
+ 'A close-up must take surrounding detailed volumes out of rendering.');
+ assert.ok(state.overview.length > 0 && state.overview.every(bank => bank.display === 'none'),
+ 'A close-up must take the detailed Milky Way overview out of rendering.');
+ assert.ok(state.stellar.length > 0 && state.stellar.every(bank => bank.display === 'none'),
+ 'A close-up must take the surrounding direct-star field out of rendering.');
+ return state;
+};
+const focus = async () => {
+ await page.locator('.planet-sidebar-search').fill('Omega Centauri');
+ await page.locator('.planet-object-link[data-prepared-focus-id="omega-centauri"]').click();
+ await settle();
+};
+const report: Record = { base, viewport, browser: browser.version(), inputPins: pins, errors, failed };
+const retainedCounts: { stage: string; elements: number; planes: number; stars: number }[] = [];
+report.retainedCounts = retainedCounts;
+const assertElementBudget = async (stage: string) => {
+ const count = await page.locator('[data-volume-lens-object="omega-centauri"]').evaluate(root => ({
+ elements: 1 + root.querySelectorAll('*').length,
+ planes: root.querySelectorAll('.css-volume-mesh > s').length,
+ stars: root.querySelectorAll('[data-catalogue-source]').length,
+ }));
+ retainedCounts.push({ stage, ...count });
+ assert.ok(count.planes > 0, `${stage}: Omega must retain real XYZ slabs.`);
+ assert.ok(count.elements <= 500, `${stage}: Omega retains ${count.elements} elements; the inclusive budget is 500.`);
+ assert.equal(count.stars, 0, 'Omega starlight is already baked into the slabs.');
+};
+try {
+ assert.equal((await page.goto(`${base}/sun/?focus=omega-centauri`, { waitUntil: 'domcontentloaded' }))?.status(), 200);
+ await page.waitForFunction(() => window.__sun?.ready, null, { timeout: 180_000 });
+ await page.locator('[data-focus-lens-bank="omega-centauri"]').waitFor({ state: 'visible' });
+ await page.waitForFunction(({ frame, target, radiusM }) => {
+ const position = window.__cssearthTest.object('sun').camera.captureWorldCamera(frame).pose.positionM;
+ const distance = Math.hypot(...position.map((value, axis) => value - target[axis]!));
+ const radius = window.__cssearthTest.physicalCamera('sun').focal * radiusM / distance;
+ return radius >= 150 && radius <= 300;
+ }, parameters, { timeout: 30_000 });
+ await settle();
+ const cold = await measure(); report.cold = cold;
+ // At this fixed viewport the authored cloud must occupy a useful foreground size.
+ // The former unloaded-bounds arrival measured only 64 px and fails this condition.
+ assert.ok(cold.projectedRadiusPixels >= 150 && cold.projectedRadiusPixels <= 300);
+ report.coldContext = await assertCloseContext();
+ await assertElementBudget('cold arrival');
+ await page.screenshot({ path: resolve(out, 'native-arrival.png') });
+ await focus();
+ const warm = await measure(); report.warm = warm;
+ await assertElementBudget('warm reselection');
+ assert.ok(Math.abs(cold.distanceM / warm.distanceM - 1) < .01, 'Cold arrival and ordinary reselection must use the same prepared radius.');
+ const beforeDrag = JSON.stringify(warm.world.pose.orientationXyzw);
+ await page.mouse.move(720, 500); await page.mouse.down();
+ await page.mouse.move(820, 530, { steps: 10 }); await page.mouse.up(); await settle();
+ const dragged = await measure(); report.dragged = dragged;
+ await assertElementBudget('orbit');
+ assert.notEqual(JSON.stringify(dragged.world.pose.orientationXyzw), beforeDrag, 'Ordinary drag must orbit the focused object.');
+ await page.mouse.wheel(0, -5000); await settle();
+ const zoomed = await measure(); report.zoomed = zoomed;
+ await assertElementBudget('close zoom');
+ assert.ok(zoomed.distanceM < dragged.distanceM, 'Ordinary wheel input must approach the cluster.');
+ assert.ok(zoomed.distanceRadii >= .05 * (1 - 1e-8), 'Wheel input must respect the existing shared volume minimum.');
+ await focus();
+ const restored = await measure(); report.reselected = restored;
+ assert.ok(Math.abs(restored.distanceM / warm.distanceM - 1) < .01, 'Reselection must recover framing after zoom.');
+ report.reselectedContext = await assertCloseContext();
+ const outwardDelta = await page.evaluate(distanceRadii => Math.log(100 / distanceRadii) /
+ window.__cssearthTest.required(window.__cssearthTest.object('sun').camera.stats().dolly, 'dolly diagnostics').wheelStepPerDelta,
+ restored.distanceRadii);
+ await page.mouse.move(720, 500);
+ await page.mouse.wheel(0, outwardDelta); await settle();
+ const far = await measure(); report.zoomedOut = far;
+ assert.ok(far.distanceRadii >= 32, 'Ordinary outward wheel input must leave the close-up.');
+ await page.waitForFunction(() => [...document.querySelectorAll('[data-volume-lens-object]')].some(root =>
+ root.dataset.volumeLensObject !== 'omega-centauri' && getComputedStyle(root).display !== 'none' &&
+ Number(getComputedStyle(root).opacity) > 0), null, { timeout: 30_000 });
+ report.farContext = await contextVisibility();
+ assert.ok((await contextVisibility()).overview.some(bank => bank.display !== 'none' && bank.opacity > 0),
+ 'Zooming out must restore the detailed galaxy context.');
+ await focus();
+ report.returnedContext = await assertCloseContext();
+ await assertElementBudget('return from galaxy context');
+ for (const lens of payload.lenses) {
+ await page.locator(`[data-focus-lens-bank="omega-centauri"] [data-focus-lens][value="${lens.id}"]`).click();
+ await page.waitForFunction(id => document.querySelector('[data-volume-lens-object="omega-centauri"]')?.getAttribute('data-selected-lens') === id, lens.id);
+ await settle();
+ await assertElementBudget(`lens ${lens.id}`);
+ }
+ report.rendering = await page.evaluate(() => ({
+ totalElements: document.querySelectorAll('*').length,
+ volumes: [...document.querySelectorAll('[data-volume-lens-object]')].map(root => ({
+ id: root.dataset.volumeLensObject, display: getComputedStyle(root).display, opacity: getComputedStyle(root).opacity,
+ elements: 1 + root.querySelectorAll('*').length, planes: root.querySelectorAll('.css-volume-mesh > s').length,
+ stars: root.querySelectorAll('[data-catalogue-source]').length,
+ })),
+ galaxyOverview: [...document.querySelectorAll('[data-volume-opacity]')].map(root => ({
+ opacity: root.dataset.volumeOpacity, display: getComputedStyle(root).display, elements: root.querySelectorAll('*').length,
+ })),
+ }));
+ assert.equal(await page.evaluate(() => window.__cssEarth?.mountedObjectCount), 1);
+ assert.equal(await page.locator('.planet-stage .polycss-camera').count(), 1);
+ assert.deepEqual(errors, []); assert.deepEqual(failed, []);
+ report.result = 'passed';
+ console.log('OMEGA_NATIVE_FOCUS_PASSED', JSON.stringify({ cold: cold.projectedRadiusPixels, warm: warm.projectedRadiusPixels, zoomDistanceRadii: zoomed.distanceRadii }));
+} catch (error) {
+ report.result = 'failed'; report.failure = error instanceof Error ? error.message : String(error);
+ if (await page.evaluate(() => !!window.__sun?.ready).catch(() => false)) report.failureState = await measure();
+ throw error;
+} finally {
+ await writeFile(resolve(out, 'native-focus.json'), JSON.stringify(report, null, 2) + '\n');
+ await browser.close();
+}
diff --git a/tests/objects/browser/omega-centauri/promotion-inspection-browser.mts b/tests/objects/browser/omega-centauri/promotion-inspection-browser.mts
new file mode 100644
index 0000000000..4e9d4502ea
--- /dev/null
+++ b/tests/objects/browser/omega-centauri/promotion-inspection-browser.mts
@@ -0,0 +1,6 @@
+/** Inspect both Omega Centauri lenses in the shared application's native scene. */
+import { resolve } from 'node:path';
+import { inspectVolumeLensBank } from '../volume-lens-bank/promotion-inspection.mts';
+
+await inspectVolumeLensBank('omega-centauri', process.argv[2] ?? 'http://127.0.0.1:4210',
+ resolve(process.argv[3] ?? 'src/objects/omega-centauri/evidence/app-inspection'));
diff --git a/tests/objects/browser/volume-lens-bank/promotion-inspection.mts b/tests/objects/browser/volume-lens-bank/promotion-inspection.mts
index 668d661ae3..f51c0415ae 100644
--- a/tests/objects/browser/volume-lens-bank/promotion-inspection.mts
+++ b/tests/objects/browser/volume-lens-bank/promotion-inspection.mts
@@ -3,7 +3,7 @@
*
* Opens the native `/sun/?focus=` route, so the server fragment must retain the object's complete
* physical-host chain before the lens bank mounts, then captures the Earth view, an oblique
- * view, both exact 90-degree side views, every lens switch and stars on and off, and writes them with a
+ * view, both exact 90-degree side axes, every lens switch and stars on and off when present, and writes them with a
* machine-readable record. Object packages call it through their own inspection entry.
*/
import assert from 'node:assert/strict';
@@ -31,7 +31,9 @@ export async function inspectVolumeLensBank(OBJECT_ID: string, base: string, dir
inputPins.push({ path, bytes: bytes.length, sha256: createHash('sha256').update(bytes).digest('hex') });
return JSON.parse(bytes.toString('utf8'));
}
- const context = parsePreparedWorldContext(JSON.parse(await readFile('src/objects/sun/prepared/world-context.json', 'utf8')));
+ const contextPath = 'src/objects/sun/prepared/world-context.json', contextBytes = await readFile(contextPath);
+ inputPins.push({ path: contextPath, bytes: contextBytes.length, sha256: createHash('sha256').update(contextBytes).digest('hex') });
+ const context = parsePreparedWorldContext(JSON.parse(contextBytes.toString('utf8')));
const payload = validatePreparedVolumeLenses(requireRecord(await readPrepared('lenses')).data);
const provenance = validateObjectProvenance(await readPrepared('provenance'), OBJECT_ID);
const presentation = parsePreparedVolumePresentation(await readPrepared('presentation'), payload, provenance);
@@ -45,7 +47,7 @@ export async function inspectVolumeLensBank(OBJECT_ID: string, base: string, dir
page.on('pageerror', error => errors.push(error.message.split('\n')[0]!));
page.on('response', response => { if (response.status() >= 400) failed.push(`${response.status()} ${response.url()}`); });
- interface Capture { name: string; path: string; url: string; view: string; lens: string; textures?: number }
+ interface Capture { name: string; path: string; bytes: number; sha256: string; camera: string; url: string; view: string; lens: string; textures?: number }
const report: {
objectId: string; base: string; browser: string; viewport: typeof viewport; deviceScaleFactor: number;
revision: string; dirtyFiles: string[]; inputPins: typeof inputPins; navigation: string;
@@ -67,7 +69,9 @@ export async function inspectVolumeLensBank(OBJECT_ID: string, base: string, dir
await settle();
const path = resolve(directory, `${name}.png`);
await page.screenshot({ path });
- report.captures.push({ name, path: relative(process.cwd(), path), url: page.url(), view, lens, ...(textures === undefined ? {} : { textures }) });
+ const bytes = await readFile(path);
+ report.captures.push({ name, path: relative(process.cwd(), path), bytes: bytes.length, sha256: createHash('sha256').update(bytes).digest('hex'),
+ camera: await cameraState(), url: page.url(), view, lens, ...(textures === undefined ? {} : { textures }) });
await write();
console.log(`${LOG}_INSPECTION_CAPTURE ${name} ${view}`);
}
@@ -88,16 +92,20 @@ export async function inspectVolumeLensBank(OBJECT_ID: string, base: string, dir
const focal = () => page.evaluate(() => window.__cssearthTest.physicalCamera('sun').focal);
const extent = [0, 1, 2].map(axis => Math.max(Math.abs(frame.boundsUnits.min[axis]!), Math.abs(frame.boundsUnits.max[axis]!)));
/** Same framing rule as the shared nebula delivery run, so the views are comparable. */
- function framingDistance(angle: number, focalPixels: number) {
- const horizontal = extent[0]! * Math.abs(Math.cos(angle)) + extent[2]! * Math.abs(Math.sin(angle));
- const depth = extent[0]! * Math.abs(Math.sin(angle)) + extent[2]! * Math.abs(Math.cos(angle));
- return Math.max(payload.framingRadiusUnits * 4, depth + focalPixels * Math.max(horizontal / 330, extent[1]! / 400));
+ function framingDistance(angle: number, focalPixels: number, axis: 'x' | 'y' = 'y') {
+ const cosine = Math.abs(Math.cos(angle)), sine = Math.abs(Math.sin(angle));
+ const horizontal = axis === 'y' ? extent[0]! * cosine + extent[2]! * sine : extent[0]!;
+ const vertical = axis === 'x' ? extent[1]! * cosine + extent[2]! * sine : extent[1]!;
+ const depth = extent[axis === 'y' ? 0 : 1]! * sine + extent[2]! * cosine;
+ return Math.max(payload.framingRadiusUnits * 4, depth + focalPixels * Math.max(horizontal / 330, vertical / 400));
}
- /** Orbit about the model's local Y axis, keeping its origin centered. */
- async function apply(distance: number, angle = 0) {
+ /** Orbit about either local axis, keeping the model origin centered. */
+ async function apply(distance: number, angle = 0, axis: 'x' | 'y' = 'y') {
const pose = cameraPoseToReferenceFrame({
- positionM: [-Math.sin(angle) * distance * frame.metersPerUnit, 0, -Math.cos(angle) * distance * frame.metersPerUnit],
- orientationXyzw: [Math.cos(angle / 2), 0, -Math.sin(angle / 2), 0],
+ positionM: axis === 'y'
+ ? [-Math.sin(angle) * distance * frame.metersPerUnit, 0, -Math.cos(angle) * distance * frame.metersPerUnit]
+ : [0, Math.sin(angle) * distance * frame.metersPerUnit, -Math.cos(angle) * distance * frame.metersPerUnit],
+ orientationXyzw: axis === 'y' ? [Math.cos(angle / 2), 0, -Math.sin(angle / 2), 0] : [Math.cos(angle / 2), 0, 0, -Math.sin(angle / 2)],
}, frame);
await page.evaluate(({ world, reference }) => window.__cssearthTest.object('sun').camera.applyWorldCamera(world, reference),
{ world: { referenceFrame: frame.referenceFrame, epochJdTt: frame.epochJdTt, pose }, reference: context.frame });
@@ -116,7 +124,15 @@ export async function inspectVolumeLensBank(OBJECT_ID: string, base: string, dir
const controls = page.locator(`[data-focus-lens-bank="${OBJECT_ID}"]`);
await controls.waitFor({ state: 'visible' });
const bank = page.locator(`[data-volume-lens-object="${OBJECT_ID}"]`);
+ await page.waitForFunction(id => {
+ const element = document.querySelector(`[data-volume-lens-object="${id}"]`);
+ return element !== null && getComputedStyle(element).display === 'block';
+ }, OBJECT_ID, { timeout: 60_000 });
assert.equal(await bank.evaluate(element => getComputedStyle(element).display), 'block');
+ assert.equal(await page.evaluate(() => window.__cssEarth?.mountedObjectCount), 1, 'Exactly one object scene is mounted.');
+ assert.equal(await page.locator('.planet-stage').count(), 1, 'The shared stage is unique.');
+ assert.equal(await page.locator('.planet-stage .polycss-camera').count(), 1, 'The shared world camera is unique.');
+ assert.equal(await page.locator('canvas').count(), 0, 'The volume is rendered without canvas.');
assert.equal(await controls.locator('[data-focus-lens]').count(), payload.lenses.length, 'Every prepared lens is offered.');
const focalPixels = await focal();
@@ -155,6 +171,8 @@ export async function inspectVolumeLensBank(OBJECT_ID: string, base: string, dir
const textures = await decodeCssImages(cloud);
await capture(`view-${name}`, view, defaultLens, textures);
}
+ await apply(framingDistance(Math.PI / 2, focalPixels, 'x'), Math.PI / 2, 'x');
+ await capture('view-side-y', 'exact local Y-axis side (90° about X)', defaultLens, await decodeCssImages(cloud));
// Stars are a shared toggle over the same geometry: on, then off, at the Earth view.
await apply(front);
@@ -162,23 +180,25 @@ export async function inspectVolumeLensBank(OBJECT_ID: string, base: string, dir
assert.equal(total, payload.lenses[0]!.stars.points.length, 'The prepared catalogue field is mounted in full.');
report.stars.total = total;
const stars = controls.locator('[data-focus-stars]');
- assert.equal(await stars.count(), 1, 'A nonempty compact-light bank retains its shared toggle.');
- const toggle = controls.locator('label:has([data-focus-stars])');
- if (!await stars.isChecked()) await toggle.click();
- const pointRoot = bank.locator('.prepared-catalogue-points');
- report.stars.rootDisplayOn = await pointRoot.evaluate(element => getComputedStyle(element).display);
- assert.equal(report.stars.rootDisplayOn, 'block');
- report.stars.visibleWithStarsOn = await bank.locator('[data-catalogue-source]')
- .evaluateAll(nodes => nodes.filter(node => getComputedStyle(node).visibility === 'visible').length);
- assert.ok(report.stars.visibleWithStarsOn > 0, 'The prepared stellar field must be visible.');
- await capture('stars-on', 'earth, stars on', defaultLens);
- await toggle.click();
- assert.equal(await stars.isChecked(), false);
- report.stars.rootDisplayOff = await pointRoot.evaluate(element => getComputedStyle(element).display);
- assert.equal(report.stars.rootDisplayOff, 'none');
- await capture('stars-off', 'earth, stars off', defaultLens);
- await toggle.click();
+ assert.equal(await stars.count(), total > 0 ? 1 : 0, 'Only a nonempty compact-light bank offers a stars toggle.');
+ if (total > 0) {
+ const toggle = controls.locator('label:has([data-focus-stars])');
+ if (!await stars.isChecked()) await toggle.click();
+ const pointRoot = bank.locator('.prepared-catalogue-points');
+ report.stars.rootDisplayOn = await pointRoot.evaluate(element => getComputedStyle(element).display);
+ assert.equal(report.stars.rootDisplayOn, 'block');
+ report.stars.visibleWithStarsOn = await bank.locator('[data-catalogue-source]')
+ .evaluateAll(nodes => nodes.filter(node => getComputedStyle(node).visibility === 'visible').length);
+ assert.ok(report.stars.visibleWithStarsOn > 0, 'The prepared stellar field must be visible.');
+ await capture('stars-on', 'earth, stars on', defaultLens);
+ await toggle.click();
+ assert.equal(await stars.isChecked(), false);
+ report.stars.rootDisplayOff = await pointRoot.evaluate(element => getComputedStyle(element).display);
+ assert.equal(report.stars.rootDisplayOff, 'none');
+ await capture('stars-off', 'earth, stars off', defaultLens);
+ await toggle.click();
+ }
assert.deepEqual(errors, [], 'The inspected route must raise no page error.');
assert.deepEqual(failed, [], 'The inspected route must issue no failing request.');
report.result = 'passed';
diff --git a/tools/nebula/application/backend.ts b/tools/nebula/application/backend.ts
index 5cff76c14f..488a83ddd8 100644
--- a/tools/nebula/application/backend.ts
+++ b/tools/nebula/application/backend.ts
@@ -1,3 +1,4 @@
+import { CSS_COMPILER_RENDER_BUDGET } from '../../../src/renderers/css/volume/compiler-render-budget.ts';
/** Concrete cssEarth representation and point-profile backend; numerical replay stays package-owned. */
import { compileCssVolume } from '../../../src/renderers/css/preparation/volume.ts';
import { validatePreparedCssVolume } from '../../../src/renderers/css/volume/validation.ts';
@@ -5,6 +6,7 @@ import type { CompilerBakeBackend } from '@cssearth/volume-bake/compiler/bake';
import { prepareCompilerStarSprites } from './star-sprites.ts';
import { decodeFits } from './fits.ts';
export const nebulaBakeBackend = {
+ renderBudget: CSS_COMPILER_RENDER_BUDGET,
compileVolume: (input: Parameters[0]) =>
validatePreparedCssVolume(compileCssVolume({ ...input, recipe: { anchors: [] } })),
readVolume: validatePreparedCssVolume,
diff --git a/tools/nebula/application/element-budget.cases.ts b/tools/nebula/application/element-budget.cases.ts
new file mode 100644
index 0000000000..ff10d3fa4b
--- /dev/null
+++ b/tools/nebula/application/element-budget.cases.ts
@@ -0,0 +1,88 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import type { CompilerBakeResult } from '@cssearth/volume-core/contracts/compiler-bake';
+import { createRenderElementBudget } from '@cssearth/volume-core/contracts/render-element-budget';
+import { CSS_COMPILER_RENDER_BUDGET } from '../../../src/renderers/css/volume/compiler-render-budget.js';
+import type { PreparedCssVolume, VolumeVector } from '../../../src/renderers/css/volume/types.js';
+import type { PreparedVolumeLenses } from '../../../src/renderers/css/volume/prepared-volume-lenses.js';
+import { assertCompilerDeliveryElementBudget } from './element-budget.ts';
+
+const axes = ['x', 'y', 'z'] as const;
+const frame = { referenceFrame: 'lab-sky-west-north-toward', epochJdTt: 2451545, metersPerUnit: 1,
+ originM: [0, 0, 0] as const, localToReferenceXyzw: [0, 0, 0, 1] as const,
+ boundsUnits: { min: [-1, -1, -1] as const, max: [1, 1, 1] as const } };
+function sampling(counts = { x: 50, y: 50, z: 50 }, stars = 3): CompilerBakeResult['sampling'] {
+ return { imageWidth: 512, samplesPerSlab: 4, sliceCounts: counts,
+ renderBudget: createRenderElementBudget(CSS_COMPILER_RENDER_BUDGET, stars, counts.x + counts.y + counts.z) };
+}
+function volume(id: string, counts: Record, impostorCount: number): PreparedCssVolume {
+ const resource = (path: string) => ({ path, width: 1, height: 1, bytes: 1, sha256: 'a'.repeat(64) });
+ const views = Array.from({ length: impostorCount }, (_, index) => ({ id: `view-${index}`, texturePath: `${id}/view-${index}.png`,
+ back: [0, 0, 1] as VolumeVector, right: [1, 0, 0] as VolumeVector, down: [0, -1, 0] as VolumeVector }));
+ return { schema: 'cssearth-css-volume@1', id, frame, anchors: [], provenance: {}, approximation: {},
+ stacks: axes.map(axis => ({ axis, leaves: Array.from({ length: counts[axis] }, (_, index) => ({
+ id: `${axis}-${index}`, centerUnits: [0, 0, 0], texturePath: `${id}/${axis}.png`, widthPx: 1, heightPx: 1,
+ style: { width: '1px', height: '1px', transform: 'matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)',
+ backgroundSize: '1px 1px', backgroundPosition: '0px 0px' },
+ })) })),
+ resources: [...axes.map(axis => resource(`${id}/${axis}.png`)), ...views.map(view => resource(view.texturePath))],
+ ...(impostorCount ? { impostors: { schema: 'cssearth-volume-impostors@1' as const, radiusUnits: 1,
+ fullBelowDiameterPixels: 16, volumeAboveDiameterPixels: 32, views } } : {}),
+ };
+}
+function bank(counts = { x: 50, y: 50, z: 50 }, stars = 3, impostors = 26): PreparedVolumeLenses {
+ return { schema: 'cssearth-volume-lenses@1', id: 'fixture', defaultLens: 'first', framingRadiusUnits: 1, starsEnabled: false,
+ lenses: ['first', 'second'].map(id => ({ id, label: id, title: id, description: 'Test delivery', sourceUrl: 'https://example.org/source',
+ volume: volume(id, counts, impostors), brightness: { overall: 1, x: 1, y: 1, z: 1 },
+ stars: { frame, points: Array.from({ length: stars }, (_, index) => ({ id: `star-${index}`,
+ positionUnits: [0, 0, 0], sizePx: 1, opacity: 0, colorCss: '#ffffff' })) },
+ })) };
+}
+
+test('final delivery counts all retained XYZ copies and hidden stars once across shared lenses', () => {
+ assert.deepEqual(assertCompilerDeliveryElementBudget(sampling(), bank()),
+ { starCount: 3, slabCount: 150, impostorCount: 26, totalElements: 499 });
+});
+test('transparent slab pruning preserves the saved plan receipt while lowering actual delivery cost', () => {
+ assert.deepEqual(assertCompilerDeliveryElementBudget(sampling(), bank({ x: 49, y: 48, z: 47 })),
+ { starCount: 3, slabCount: 144, impostorCount: 26, totalElements: 481 });
+});
+test('a bank without impostors does not report the three absent LOD wrappers', () => {
+ assert.deepEqual(assertCompilerDeliveryElementBudget(sampling(), bank(undefined, 3, 0)),
+ { starCount: 3, slabCount: 150, impostorCount: 0, totalElements: 470 });
+});
+test('a delivery cannot add field stars beyond the compiler reservation, even when disabled', () => {
+ assert.throws(() => assertCompilerDeliveryElementBudget(sampling(), bank(undefined, 4)), /star reservation/);
+});
+test('delivery cannot move or add leaves outside its admitted XYZ count profile', () => {
+ assert.throws(() => assertCompilerDeliveryElementBudget(sampling(), bank({ x: 51, y: 49, z: 50 })), /planned XYZ/);
+ assert.throws(() => assertCompilerDeliveryElementBudget(sampling(), bank({ x: 51, y: 50, z: 50 })), /planned XYZ/);
+});
+test('a second retained topology is rejected even when each lens independently fits the cap', () => {
+ const data = bank({ x: 1, y: 1, z: 1 }, 0), second = data.lenses[1]!;
+ const altered = { ...second, volume: { ...second.volume, stacks: second.volume.stacks.map(stack => ({ ...stack,
+ leaves: stack.leaves.map(leaf => ({ ...leaf, centerUnits: [0, 0, .1] as VolumeVector })) })) } };
+ assert.throws(() => assertCompilerDeliveryElementBudget(sampling({ x: 1, y: 1, z: 1 }, 0),
+ { ...data, lenses: [data.lenses[0]!, altered] }), /one retained topology/);
+});
+test('separate occulting roots and excess impostors are outside the tested CSS cost profile', () => {
+ const data = bank();
+ assert.throws(() => assertCompilerDeliveryElementBudget(sampling(), { ...data,
+ lenses: data.lenses.map(lens => ({ ...lens, occultingCentreUnits: [0, 0, 0] })) }), /occulting/);
+ assert.throws(() => assertCompilerDeliveryElementBudget(sampling(), bank(undefined, 3, 27)), /26 impostors/);
+});
+test('saved counters and host profile cannot conceal an over-budget or unrelated receipt', () => {
+ const saved = sampling();
+ assert.throws(() => assertCompilerDeliveryElementBudget({ ...saved,
+ renderBudget: { ...saved.renderBudget!, totalElements: 499 } }, bank()), /budget differs/);
+ const relaxed = { ...CSS_COMPILER_RENDER_BUDGET, maximumElements: 501 };
+ assert.throws(() => assertCompilerDeliveryElementBudget({ ...saved,
+ renderBudget: createRenderElementBudget(relaxed, 4, 150) }, bank(undefined, 4)), /profile differs/);
+});
+test('historical omission does not impose a new quota or mutate an accepted delivery', () => {
+ const data = bank({ x: 170, y: 170, z: 170 }, 500), before = JSON.stringify(data);
+ assert.equal(assertCompilerDeliveryElementBudget(undefined, data), undefined);
+ assert.equal(assertCompilerDeliveryElementBudget({ imageWidth: 512, samplesPerSlab: 4,
+ sliceCounts: { x: 170, y: 170, z: 170 } }, data), undefined);
+ assert.equal(JSON.stringify(data), before);
+});
diff --git a/tools/nebula/application/element-budget.test.ts b/tools/nebula/application/element-budget.test.ts
new file mode 100644
index 0000000000..1e04fc7dd7
--- /dev/null
+++ b/tools/nebula/application/element-budget.test.ts
@@ -0,0 +1,31 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { mkdtemp, mkdir, rm } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { spawnSync } from 'node:child_process';
+import { build } from 'esbuild';
+
+// CI invokes this file directly. Bundle the typed cases just as the preparation runner does,
+// so renderer-source .js imports resolve to their TypeScript owners without a runtime loader.
+test('element-budget executes all 9 typed application cases', async t => {
+ const root = process.cwd(), parent = resolve(root, '.local/nebula-application-tests');
+ await mkdir(parent, { recursive: true });
+ const directory = await mkdtemp(resolve(parent, 'element-budget-'));
+ t.after(() => rm(directory, { recursive: true, force: true }));
+ const outfile = resolve(directory, 'cases.test.mjs');
+ await build({ entryPoints: [resolve(root, 'tools/nebula/application/element-budget.cases.ts')], outfile,
+ bundle: true, platform: 'node', format: 'esm', target: 'node22', packages: 'external',
+ plugins: [{ name: 'retain-native-modules', setup(builder) {
+ builder.onResolve({ filter: /\.m[jt]s$/ }, args => ({ path: resolve(args.resolveDir, args.path), external: true }));
+ } }],
+ });
+ const env = { ...process.env }; delete env.NODE_TEST_CONTEXT;
+ const result = spawnSync(process.execPath, ['--test', outfile], { cwd: root, env, encoding: 'utf8', timeout: 30000 });
+ if (result.error) throw result.error;
+ const output = result.stdout + result.stderr;
+ assert.equal(result.status, 0, output);
+ assert.match(output, /# tests 9\b/, output);
+ assert.match(output, /# pass 9\b/, output);
+ assert.match(output, /# fail 0\b/, output);
+ console.log(output);
+});
diff --git a/tools/nebula/application/element-budget.ts b/tools/nebula/application/element-budget.ts
new file mode 100644
index 0000000000..5235dcda69
--- /dev/null
+++ b/tools/nebula/application/element-budget.ts
@@ -0,0 +1,39 @@
+import type { CompilerBakeResult } from '@cssearth/volume-core/contracts/compiler-bake';
+import { readRenderElementBudget } from '@cssearth/volume-core/contracts/render-element-budget';
+import { CSS_COMPILER_RENDER_BUDGET } from '../../../src/renderers/css/volume/compiler-render-budget.js';
+import { samePreparedVolumeTopology } from '../../../src/renderers/css/volume/prepared-volume-lod.js';
+import { validatePreparedVolumeLenses, type PreparedVolumeLenses } from '../../../src/renderers/css/volume/prepared-volume-lenses.js';
+
+const AXES = ['x', 'y', 'z'] as const;
+// Renderer conformance counts these retained wrappers/markers, including the bank root and empty star wrapper.
+const BASE_ELEMENTS = 17, LOD_ELEMENTS = 3, MAX_IMPOSTORS = 26;
+
+/** Final admission is checked after every delivered lens has been assembled. */
+export function assertCompilerDeliveryElementBudget(
+ sampling: CompilerBakeResult['sampling'] | undefined, data: PreparedVolumeLenses,
+): { starCount: number; slabCount: number; impostorCount: number; totalElements: number } | undefined {
+ if (sampling?.renderBudget === undefined) return undefined;
+ const bank = validatePreparedVolumeLenses(data), first = bank.lenses[0]!;
+ if (bank.lenses.some(lens => lens.occultingCentreUnits !== undefined))
+ throw new TypeError('The compiler render budget does not admit separate occulting roots.');
+ if (bank.lenses.some(lens => !samePreparedVolumeTopology(first.volume, lens.volume)))
+ throw new TypeError('The compiler render budget requires one retained topology across every delivered lens.');
+ const impostorCount = first.volume.impostors?.views.length ?? 0;
+ if (impostorCount > MAX_IMPOSTORS) throw new TypeError('The compiler render budget admits at most 26 impostors.');
+ let slabCount = 0, plannedSlabs = 0;
+ for (const axis of AXES) {
+ const planned = sampling.sliceCounts[axis], actual = first.volume.stacks.find(stack => stack.axis === axis)!.leaves.length;
+ if (!Number.isSafeInteger(planned) || planned < 1 || actual > planned)
+ throw new TypeError('Delivered leaves exceed the compiler planned XYZ count profile.');
+ plannedSlabs += planned; slabCount += actual;
+ }
+ // The renderer retains one shared catalogue pool; its validator proves identical point geometry for all lenses.
+ // Count zero-alpha and disabled points as well. Field-star replacement has already happened at this boundary.
+ const starCount = first.stars.points.length;
+ readRenderElementBudget(sampling.renderBudget, starCount, plannedSlabs, CSS_COMPILER_RENDER_BUDGET);
+ const totalElements = BASE_ELEMENTS + (first.volume.impostors ? LOD_ELEMENTS : 0) + impostorCount + CSS_COMPILER_RENDER_BUDGET.elementsPerSlab * slabCount +
+ CSS_COMPILER_RENDER_BUDGET.elementsPerStar * starCount;
+ if (!Number.isSafeInteger(totalElements) || totalElements > CSS_COMPILER_RENDER_BUDGET.maximumElements)
+ throw new RangeError(`Delivered compiler bank retains ${totalElements} elements; limit is ${CSS_COMPILER_RENDER_BUDGET.maximumElements}.`);
+ return { starCount, slabCount, impostorCount, totalElements };
+}
diff --git a/tools/nebula/application/nebula-frame.ts b/tools/nebula/application/nebula-frame.ts
index edbb9c069d..c6796eb641 100644
--- a/tools/nebula/application/nebula-frame.ts
+++ b/tools/nebula/application/nebula-frame.ts
@@ -18,7 +18,8 @@ function quaternion(x: VolumeVector, y: VolumeVector, z: VolumeVector): [number,
if (e > i) { const s = Math.sqrt(1+e-a-i)*2; return [(b+d)/s,s/4,(f+h)/s,(c-g)/s]; }
const s = Math.sqrt(1+i-a-e)*2; return [(c+g)/s,(f+h)/s,s/4,(d-b)/s];
}
-/** The lab's west/north/away image frame is reflected into proper east/north/away ICRS. */
+const towardFrame = (source?: Pick) => source?.referenceFrame === 'lab-sky-west-north-toward';
+/** Historical angular source frames reflect X; physical west/north/toward frames rotate X and Z. */
export function embedNebulaFrame(source: DensityVolumeFrame, sky: NebulaSkyFrame,
originUnits: VolumeVector = [0,0,0]): DensityVolumeFrame {
if (!sky.centerIcrsDegrees.every(Number.isFinite) || sky.centerIcrsDegrees[0] < 0 || sky.centerIcrsDegrees[0] >= 360 ||
@@ -35,13 +36,15 @@ export function embedNebulaFrame(source: DensityVolumeFrame, sky: NebulaSkyFrame
localToReferenceXyzw: quaternion(x,y,away),
originM: vector(away.map((n,i) => n*sky.distancePc*METERS_PER_PARSEC +
(-x[i]!*originUnits[0]+y[i]!*originUnits[1]+n*originUnits[2])*metersPerUnit)),
- boundsUnits: { min: [-source.boundsUnits.max[0],source.boundsUnits.min[1],source.boundsUnits.min[2]],
- max: [-source.boundsUnits.min[0],source.boundsUnits.max[1],source.boundsUnits.max[2]] } };
+ boundsUnits: { min: [-source.boundsUnits.max[0],source.boundsUnits.min[1],towardFrame(source) ? -source.boundsUnits.max[2] : source.boundsUnits.min[2]],
+ max: [-source.boundsUnits.min[0],source.boundsUnits.max[1],towardFrame(source) ? -source.boundsUnits.min[2] : source.boundsUnits.max[2]] } };
}
-export const reflectNebulaPoint = (p: VolumeVector): [number,number,number] => [-p[0],p[1],p[2]];
+export const reflectNebulaPoint = (p: VolumeVector, source?: Pick): [number,number,number] =>
+ [-p[0],p[1],towardFrame(source) ? -p[2] : p[2]];
/** PolyCSS encodes physical [x,y,z] as CSS [y,x,z]; reflect the CSS matrix's second row offline. */
export function embedNebulaVolume(volume: PreparedCssVolume, frame: DensityVolumeFrame, id: string, prefix: string): PreparedCssVolume {
- return { ...volume, id, frame, anchors: volume.anchors?.map(a => ({ ...a, positionUnits: reflectNebulaPoint(a.positionUnits) })),
+ const toward = towardFrame(volume.frame);
+ return { ...volume, id, frame, anchors: volume.anchors?.map(a => ({ ...a, positionUnits: reflectNebulaPoint(a.positionUnits, volume.frame) })),
resources: volume.resources.map(r => ({ ...r, path: `${prefix}/${r.path}` })),
stacks: volume.stacks.map(stack => ({ axis: stack.axis, leaves: balanceVolumeSlices(stack.leaves.map(leaf => {
const match = /^matrix3d\(([^)]+)\)$/.exec(leaf.style.transform);
@@ -49,9 +52,11 @@ export function embedNebulaVolume(volume: PreparedCssVolume, frame: DensityVolum
const matrix = match[1]!.split(',').map(Number);
if (matrix.length !== 16 || !matrix.every(Number.isFinite)) throw new TypeError('Invalid prepared nebula matrix.');
for (const i of [1,5,9,13]) matrix[i] = -matrix[i]!;
+ if (toward) for (const i of [2,6,10,14]) matrix[i] = -matrix[i]!;
const b = leaf.boundsCssPixels;
- return { ...leaf, centerUnits: reflectNebulaPoint(leaf.centerUnits), texturePath: `${prefix}/${leaf.texturePath}`,
- ...(b ? { boundsCssPixels: { min: [b.min[0],-b.max[1],b.min[2]] as VolumeVector, max: [b.max[0],-b.min[1],b.max[2]] as VolumeVector } } : {}),
+ return { ...leaf, centerUnits: reflectNebulaPoint(leaf.centerUnits, volume.frame), texturePath: `${prefix}/${leaf.texturePath}`,
+ ...(b ? { boundsCssPixels: { min: [b.min[0],-b.max[1],toward ? -b.max[2] : b.min[2]] as VolumeVector,
+ max: [b.max[0],-b.min[1],toward ? -b.min[2] : b.max[2]] as VolumeVector } } : {}),
style: { ...leaf.style, transform: `matrix3d(${matrix.join(',')})` } };
}), stack.axis) })) };
}
diff --git a/tools/nebula/application/objects.cases.ts b/tools/nebula/application/objects.cases.ts
new file mode 100644
index 0000000000..5f1619cc29
--- /dev/null
+++ b/tools/nebula/application/objects.cases.ts
@@ -0,0 +1,152 @@
+import assert from 'node:assert/strict';
+import { readFile, mkdir, mkdtemp, writeFile, rm } from 'node:fs/promises';
+import { resolve, relative } from 'node:path';
+import sharp from 'sharp';
+import test from 'node:test';
+import ts from 'typescript';
+import { sanitizeVolumeProvenance } from './volume-provenance.ts';
+import { installedDeliveryMatchesRecipe } from './delivery-identity.ts';
+import { prepareNebulaObject, type NebulaResearchBackend } from './objects.ts';
+import { sha256 } from '../../../src/platform/sha256.mts';
+import { createRenderElementBudget } from '@cssearth/volume-core/contracts/render-element-budget';
+import type { CompilerBakeResult } from '@cssearth/volume-core/contracts/compiler-bake';
+import type { PreparedCssVolume } from '../../../src/renderers/css/volume/types.js';
+import { CSS_COMPILER_RENDER_BUDGET } from '../../../src/renderers/css/volume/compiler-render-budget.js';
+import { validatePreparedVolumeLenses } from '../../../src/renderers/css/volume/prepared-volume-lenses.js';
+import { assertCompilerDeliveryElementBudget } from './element-budget.ts';
+
+// Bundled by objects.test.ts, so import.meta.dirname identifies the temporary bundle.
+// The repository root remains the test process's actual working directory.
+const root = process.cwd();
+
+test('sanitizeVolumeProvenance replaces the process-pid staging directory with a stable placeholder', () => {
+ const volume = { provenance: { layout: 'x', sourceVolume: { path: 'src/objects/m1/.prepared-59471/compact/hubble-optical/lenses/hubble-optical/volume.json', sha256: 'a'.repeat(64) } } };
+ const sanitized = sanitizeVolumeProvenance(volume);
+ assert.equal(sanitized.provenance.sourceVolume.path,
+ 'src/objects/m1/.prepared-compact/compact/hubble-optical/lenses/hubble-optical/volume.json');
+ assert.doesNotMatch(sanitized.provenance.sourceVolume.path, /\.prepared-\d+/);
+ // A different process's bake must record the identical, pid-independent path.
+ const otherPid = { provenance: { layout: 'x', sourceVolume: { path: 'src/objects/m1/.prepared-1/compact/hubble-optical/lenses/hubble-optical/volume.json', sha256: 'a'.repeat(64) } } };
+ assert.deepEqual(sanitizeVolumeProvenance(otherPid), sanitized);
+});
+
+test('sanitizeVolumeProvenance leaves provenance without a staging path untouched', () => {
+ const noProvenance = { provenance: null };
+ assert.equal(sanitizeVolumeProvenance(noProvenance), noProvenance);
+ const stablePath = { provenance: { sourceVolume: { path: 'src/objects/m1/prepared/hubble-optical/volume.json', sha256: 'a'.repeat(64) } } };
+ assert.equal(sanitizeVolumeProvenance(stablePath), stablePath);
+ const noSourceVolume = { provenance: { layout: 'x' } };
+ assert.equal(sanitizeVolumeProvenance(noSourceVolume), noSourceVolume);
+ // Only a whole `.prepared-` path segment is the staging directory.
+ const lookalike = { provenance: { sourceVolume: { path: 'src/objects/m1/data.prepared-7/volume.json', sha256: 'a'.repeat(64) } } };
+ assert.equal(sanitizeVolumeProvenance(lookalike), lookalike);
+});
+
+test('consumer preparation reuses a byte-verified delivery across implementation changes but not recipe changes', () => {
+ const receipt = { recipeSha256: 'a'.repeat(64), implementationSha256: 'b'.repeat(64) };
+ assert.equal(installedDeliveryMatchesRecipe(receipt, 'a'.repeat(64)), true);
+ assert.equal(installedDeliveryMatchesRecipe({ ...receipt, implementationSha256: 'c'.repeat(64) }, 'a'.repeat(64)), true);
+ assert.equal(installedDeliveryMatchesRecipe(receipt, 'd'.repeat(64)), false);
+});
+
+test('every volume the nebula delivery validates is sanitized, and an explicit bake records the sanitizer', async () => {
+ const path = 'tools/nebula/application/objects.ts', source = await readFile(resolve(root, path), 'utf8');
+ const file = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true);
+ let validated = 0, sanitized = 0;
+ const visit = (node: ts.Node): void => {
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'validatePreparedCssVolume') {
+ validated++;
+ const parent = node.parent;
+ if (ts.isCallExpression(parent) && ts.isIdentifier(parent.expression) && parent.expression.text === 'sanitizeVolumeProvenance' &&
+ parent.arguments.length === 1 && parent.arguments[0] === node) sanitized++;
+ }
+ ts.forEachChild(node, visit);
+ };
+ visit(file);
+ assert.ok(validated > 0, 'the delivery validates at least one prepared volume');
+ assert.equal(sanitized, validated, 'each validated volume passes straight through sanitizeVolumeProvenance');
+ // An explicit bake records these owners. `--if-missing` is a consumer path: it verifies the installed byte
+ // closure and recipe without silently rebaking because unrelated runtime code changed.
+ assert.match(source, /'tools\/nebula\/application\/volume-provenance\.ts'/);
+ assert.match(source, /installed\(directory,sha256\(recipeBytes\)\)/);
+ assert.doesNotMatch(source, /installed\(directory,sha256\(recipeBytes\),implementationSha256\)/);
+});
+
+test('a prepared m1 lens bank, if baked locally, records no process-pid staging directory', async () => {
+ const path = resolve(root, 'src/objects/m1/prepared/lenses.json');
+ let bytes: string;
+ try { bytes = await readFile(path, 'utf8'); }
+ catch (error) { if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return; throw error; }
+ assert.doesNotMatch(bytes, /\.prepared-\d+(?=[\\/])/, 'A bake must never record its own staging directory name in committed-shaped provenance.');
+});
+
+test('the real installer rejects post-compiler field stars before replacing the delivered package', async t => {
+ await mkdir(resolve(root, 'output'), { recursive: true });
+ const directory = await mkdtemp(resolve(root, 'output/nebula-budget-install-'));
+ t.after(() => rm(directory, { recursive: true, force: true }));
+ const save = async (path: string, value: unknown) => {
+ const bytes = JSON.stringify(value) + '\n', target = resolve(directory, path);
+ await writeFile(target, bytes);
+ return { path: relative(root, target), sha256: sha256(bytes) };
+ };
+ await mkdir(resolve(directory, 'source'));
+ await mkdir(resolve(directory, 'prepared'));
+ const request = await save('request.json', {});
+ const frame: CompilerBakeResult['frame'] = { referenceFrame: 'lab-sky-west-north-toward', epochJdTt: 2451545,
+ originM: [0, 0, 0], localToReferenceXyzw: [0, 0, 0, 1], metersPerUnit: 1,
+ boundsUnits: { min: [-1, -1, -1], max: [1, 1, 1] } };
+ const texture = await sharp(Buffer.from([255, 255, 255, 128]), { raw: { width: 1, height: 1, channels: 4 } }).png().toBuffer();
+ const transforms = {
+ x: 'matrix3d(100,0,0,0,0,0,-100,0,0,1,0,0,-50,0,50,1)',
+ y: 'matrix3d(0,100,0,0,0,0,-100,0,1,0,0,0,0,-50,50,1)',
+ z: 'matrix3d(0,100,0,0,-100,0,0,0,0,0,1,0,50,-50,0,1)',
+ };
+ const axes = ['x', 'y', 'z'] as const;
+ for (const axis of axes) await writeFile(resolve(directory, `${axis}.png`), texture);
+ const volume: PreparedCssVolume = { schema: 'cssearth-css-volume@1', id: 'fixture', frame, anchors: [],
+ provenance: {}, approximation: {}, stacks: axes.map(axis => ({ axis, leaves: [{ id: `${axis}-0`,
+ centerUnits: [0, 0, 0], texturePath: `${axis}.png`, widthPx: 1, heightPx: 1,
+ style: { width: '1px', height: '1px', transform: transforms[axis], backgroundSize: '1px 1px', backgroundPosition: '0px 0px' } }] })),
+ resources: axes.map(axis => ({ path: `${axis}.png`, sha256: sha256(texture), bytes: texture.length, width: 1, height: 1 })) };
+ const volumePin = await save('volume.json', volume);
+ // Explicit synthetic fixture row: the real catalogue-field owner adds it after compiler admission.
+ const fieldStars = await save('field.json', { schema: 'cssearth-gaia-nebula-field@1', id: 'budget-fixture', coordinateEpochJulianYear: 2016,
+ selection: { centerIcrsDegrees: [0, 0], distancePc: 1, outerRadiusPc: 1, featherStartPc: .5, maximumStars: 1,
+ retainIds: [], limitingMagnitude: 20, fadeMagnitude: 1, referenceMagnitude: 10, referenceDiameterPx: 1, referenceFocalPixels: 100 },
+ stars: [{ sourceId: '1234567890123', raDeg: 0, decDeg: 0, distancePc: 1, distanceLowerPc: .9, distanceUpperPc: 1.1,
+ pmRaMasYr: null, pmDecMasYr: null, photGMeanMag: 10, bpRp: 1, parallaxMas: 1000, parallaxErrorMas: 1, ruwe: 1 }] });
+ await save('source/delivery.json', { schema: 'cssearth-nebula-delivery@1', id: 'budget-fixture', method: 'compiler',
+ request, inputPins: [], sky: { centerIcrsDegrees: [0, 0], distancePc: 1, imageRotationDegrees: 0, arcsecPerUnit: 1 },
+ sourceUrl: 'https://example.org/fixture', description: 'Synthetic installer regression', defaultLens: 'first',
+ framingRadiusUnits: 1, acceptedLabResult: 'fixture', attachedTo: 'sun', fieldStars });
+ const scene: CompilerBakeResult = { schema: 'cssearth-compiler-bake@1', id: 'fixture', fieldIdentity: 'a'.repeat(64), frame,
+ boundsArcsec: { min: [-1, -1, -1], max: [1, 1, 1] }, skyBoundsArcsec: { min: [-1, -1], max: [1, 1] }, spanArcsec: 2, sourceImage: { width: 512, height: 512 },
+ coordinates: { axes: ['west', 'north', 'away'], localOriginArcsec: [0, 0, 0], earthView: 'observer-at-negative-z-looking-away' },
+ neutral: volumePin, lenses: [{ id: 'first', label: 'First', volume: volumePin,
+ coverage: { positiveAlphaTexels: 3, recoloredTexels: 3, outsideImageTexels: 0 } }], stars: [], alphaSha256: 'b'.repeat(64),
+ sampling: { imageWidth: 512, samplesPerSlab: 4, sliceCounts: { x: 1, y: 1, z: 1 },
+ renderBudget: createRenderElementBudget(CSS_COMPILER_RENDER_BUDGET, 0, 3) } };
+ const backend: NebulaResearchBackend = {
+ async compiler() { return { id: scene.id, scene, sources: [{ id: 'first', label: 'First', credit: 'Fixture', page: 'https://example.org/fixture' }] }; },
+ async symmetry() { throw new Error('Compiler installer must not call symmetry.'); },
+ };
+ await writeFile(resolve(directory, 'object.json'), 'existing-descriptor\n');
+ await writeFile(resolve(directory, 'prepared/lenses.json'), 'existing-bank\n');
+ await assert.rejects(prepareNebulaObject(root, directory, false, backend), /star reservation/);
+ assert.equal(await readFile(resolve(directory, 'object.json'), 'utf8'), 'existing-descriptor\n');
+ assert.equal(await readFile(resolve(directory, 'prepared/lenses.json'), 'utf8'), 'existing-bank\n');
+ // Reserving the actual final field count admits the same prepared geometry and real packaging operations.
+ scene.sampling.renderBudget = createRenderElementBudget(CSS_COMPILER_RENDER_BUDGET, 1, 3);
+ const result = await prepareNebulaObject(root, directory, false, backend);
+ assert.equal(result.status, 'prepared');
+ const parsed: unknown = JSON.parse(await readFile(resolve(directory, 'prepared/lenses.json'), 'utf8'));
+ assert.ok(parsed && typeof parsed === 'object' && 'data' in parsed);
+ const data = validatePreparedVolumeLenses(parsed.data);
+ assert.equal(data.lenses[0]!.stars.points.length, 1);
+ assert.equal(data.lenses[0]!.volume.impostors?.views.length, 26);
+ assert.ok(data.lenses[0]!.volume.stacks.every(stack => stack.leaves[0]!.texturePath.includes('/atlases/')));
+ assert.equal(assertCompilerDeliveryElementBudget(scene.sampling, data)?.totalElements, 56);
+ const receipt: unknown = JSON.parse(await readFile(resolve(directory, 'prepared/delivery.json'), 'utf8'));
+ assert.ok(receipt && typeof receipt === 'object' && 'renderElements' in receipt);
+ assert.deepEqual(receipt.renderElements, { starCount: 1, slabCount: 3, impostorCount: 26, totalElements: 56 });
+});
diff --git a/tools/nebula/application/objects.test.ts b/tools/nebula/application/objects.test.ts
index 6689c0a62a..6cf25f9934 100644
--- a/tools/nebula/application/objects.test.ts
+++ b/tools/nebula/application/objects.test.ts
@@ -1,72 +1,31 @@
import assert from 'node:assert/strict';
-import { readFile } from 'node:fs/promises';
-import { resolve } from 'node:path';
import test from 'node:test';
-import ts from 'typescript';
-import { sanitizeVolumeProvenance } from './volume-provenance.ts';
-import { installedDeliveryMatchesRecipe } from './delivery-identity.ts';
-
-// Bundled for execution (test-preparation.mts, like every other `.test.ts` here), so `import.meta.dirname`
-// would resolve against the bundle's own output location, not this file's; the repo root is the actual cwd instead.
-const root = process.cwd();
-
-test('sanitizeVolumeProvenance replaces the process-pid staging directory with a stable placeholder', () => {
- const volume = { provenance: { layout: 'x', sourceVolume: { path: 'src/objects/m1/.prepared-59471/compact/hubble-optical/lenses/hubble-optical/volume.json', sha256: 'a'.repeat(64) } } };
- const sanitized = sanitizeVolumeProvenance(volume);
- assert.equal(sanitized.provenance.sourceVolume.path,
- 'src/objects/m1/.prepared-compact/compact/hubble-optical/lenses/hubble-optical/volume.json');
- assert.doesNotMatch(sanitized.provenance.sourceVolume.path, /\.prepared-\d+/);
- // A different process's bake must record the identical, pid-independent path.
- const otherPid = { provenance: { layout: 'x', sourceVolume: { path: 'src/objects/m1/.prepared-1/compact/hubble-optical/lenses/hubble-optical/volume.json', sha256: 'a'.repeat(64) } } };
- assert.deepEqual(sanitizeVolumeProvenance(otherPid), sanitized);
-});
-
-test('sanitizeVolumeProvenance leaves provenance without a staging path untouched', () => {
- const noProvenance = { provenance: null };
- assert.equal(sanitizeVolumeProvenance(noProvenance), noProvenance);
- const stablePath = { provenance: { sourceVolume: { path: 'src/objects/m1/prepared/hubble-optical/volume.json', sha256: 'a'.repeat(64) } } };
- assert.equal(sanitizeVolumeProvenance(stablePath), stablePath);
- const noSourceVolume = { provenance: { layout: 'x' } };
- assert.equal(sanitizeVolumeProvenance(noSourceVolume), noSourceVolume);
- // Only a whole `.prepared-` path segment is the staging directory.
- const lookalike = { provenance: { sourceVolume: { path: 'src/objects/m1/data.prepared-7/volume.json', sha256: 'a'.repeat(64) } } };
- assert.equal(sanitizeVolumeProvenance(lookalike), lookalike);
-});
-
-test('consumer preparation reuses a byte-verified delivery across implementation changes but not recipe changes', () => {
- const receipt = { recipeSha256: 'a'.repeat(64), implementationSha256: 'b'.repeat(64) };
- assert.equal(installedDeliveryMatchesRecipe(receipt, 'a'.repeat(64)), true);
- assert.equal(installedDeliveryMatchesRecipe({ ...receipt, implementationSha256: 'c'.repeat(64) }, 'a'.repeat(64)), true);
- assert.equal(installedDeliveryMatchesRecipe(receipt, 'd'.repeat(64)), false);
-});
-
-test('every volume the nebula delivery validates is sanitized, and an explicit bake records the sanitizer', async () => {
- const path = 'tools/nebula/application/objects.ts', source = await readFile(resolve(root, path), 'utf8');
- const file = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true);
- let validated = 0, sanitized = 0;
- const visit = (node: ts.Node): void => {
- if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'validatePreparedCssVolume') {
- validated++;
- const parent = node.parent;
- if (ts.isCallExpression(parent) && ts.isIdentifier(parent.expression) && parent.expression.text === 'sanitizeVolumeProvenance' &&
- parent.arguments.length === 1 && parent.arguments[0] === node) sanitized++;
- }
- ts.forEachChild(node, visit);
- };
- visit(file);
- assert.ok(validated > 0, 'the delivery validates at least one prepared volume');
- assert.equal(sanitized, validated, 'each validated volume passes straight through sanitizeVolumeProvenance');
- // An explicit bake records these owners. `--if-missing` is a consumer path: it verifies the installed byte
- // closure and recipe without silently rebaking because unrelated runtime code changed.
- assert.match(source, /'tools\/nebula\/application\/volume-provenance\.ts'/);
- assert.match(source, /installed\(directory,sha256\(recipeBytes\)\)/);
- assert.doesNotMatch(source, /installed\(directory,sha256\(recipeBytes\),implementationSha256\)/);
-});
+import { mkdtemp, mkdir, rm } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { spawnSync } from 'node:child_process';
+import { build } from 'esbuild';
-test('a prepared m1 lens bank, if baked locally, records no process-pid staging directory', async () => {
- const path = resolve(root, 'src/objects/m1/prepared/lenses.json');
- let bytes: string;
- try { bytes = await readFile(path, 'utf8'); }
- catch (error) { if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return; throw error; }
- assert.doesNotMatch(bytes, /\.prepared-\d+(?=[\\/])/, 'A bake must never record its own staging directory name in committed-shaped provenance.');
+// CI invokes this file directly. Bundle the typed cases just as the preparation runner does,
+// so renderer-source .js imports resolve to their TypeScript owners without a runtime loader.
+test('objects executes all 6 typed application cases', async t => {
+ const root = process.cwd(), parent = resolve(root, '.local/nebula-application-tests');
+ await mkdir(parent, { recursive: true });
+ const directory = await mkdtemp(resolve(parent, 'objects-'));
+ t.after(() => rm(directory, { recursive: true, force: true }));
+ const outfile = resolve(directory, 'cases.test.mjs');
+ await build({ entryPoints: [resolve(root, 'tools/nebula/application/objects.cases.ts')], outfile,
+ bundle: true, platform: 'node', format: 'esm', target: 'node22', packages: 'external',
+ plugins: [{ name: 'retain-native-modules', setup(builder) {
+ builder.onResolve({ filter: /\.m[jt]s$/ }, args => ({ path: resolve(args.resolveDir, args.path), external: true }));
+ } }],
+ });
+ const env = { ...process.env }; delete env.NODE_TEST_CONTEXT;
+ const result = spawnSync(process.execPath, ['--test', outfile], { cwd: root, env, encoding: 'utf8', timeout: 30000 });
+ if (result.error) throw result.error;
+ const output = result.stdout + result.stderr;
+ assert.equal(result.status, 0, output);
+ assert.match(output, /# tests 6\b/, output);
+ assert.match(output, /# pass 6\b/, output);
+ assert.match(output, /# fail 0\b/, output);
+ console.log(output);
});
diff --git a/tools/nebula/application/objects.ts b/tools/nebula/application/objects.ts
index 0b8b52b6aa..b5599d71c4 100644
--- a/tools/nebula/application/objects.ts
+++ b/tools/nebula/application/objects.ts
@@ -23,6 +23,7 @@ import { prepareVolumeImpostors } from '../../../src/renderers/css/preparation/v
import type { PreparedVolumeLens } from '../../../src/renderers/css/volume/prepared-volume-lenses.js';
import { embedNebulaFrame, embedNebulaVolume, reflectNebulaPoint, type NebulaSkyFrame } from './nebula-frame.ts';
import { sanitizeVolumeProvenance } from './volume-provenance.ts';
+import { assertCompilerDeliveryElementBudget } from './element-budget.ts';
const json = (v: unknown) => JSON.stringify(v, null, 2) + '\n';
const record = (v: unknown): Record => { if (!v || typeof v !== 'object' || Array.isArray(v)) throw new TypeError('Expected nebula delivery object.'); return v as Record; };
const text = (v: unknown) => { if (typeof v !== 'string' || !v) throw new TypeError('Expected nebula delivery text.'); return v; };
@@ -32,6 +33,8 @@ const finite = (v: unknown) => { if (typeof v !== 'number' || !Number.isFinite(v
const implementationFiles = [
'tools/nebula/application/package-identity.ts', 'tools/nebula/application/delivery-identity.ts', 'tools/nebula/application/objects.ts', 'tools/nebula/application/backend.ts',
'tools/nebula/application/references.ts', 'tools/nebula/application/nebula-frame.ts', 'tools/nebula/application/volume-provenance.ts',
+ 'tools/nebula/application/element-budget.ts', 'src/renderers/css/volume/compiler-render-budget.ts',
+ 'src/renderers/css/volume/prepared-volume-lod.ts',
'tools/nebula/application/star-sprites.ts', 'tools/nebula/application/fits.ts',
'src/preparation/volume/atlas.ts', 'src/renderers/css/preparation/volume.ts',
'src/renderers/css/preparation/volume-order.ts', 'src/renderers/css/preparation/volume-impostors.ts',
@@ -146,6 +149,7 @@ export async function prepareNebulaObject(root: string, directory: string, ifMis
}
const staging = resolve(directory,`.prepared-${process.pid}`); await mkdir(staging,{recursive:true});
const lenses: PreparedVolumeLens[] = []; let sourceResult = recipe.acceptedLabResult;
+ let compilerSampling: CompilerBakeResult['sampling'] | undefined;
let fieldStars: Awaited>['receipt'] | undefined;
try {
const add = async (id: string,label: string,sourceUrl: string,volumePath: string,volumeSha: string,
@@ -195,17 +199,18 @@ export async function prepareNebulaObject(root: string, directory: string, ifMis
return research.compiler(root, recipe, progress);
};
const result = await prepareResult();
+ compilerSampling = result.scene.sampling;
if ('objectId' in result && result.objectId !== recipe.id) throw new TypeError('Compact input belongs to another object.');
sourceResult = result.id;
const frame = embedNebulaFrame(result.scene.frame,recipe.sky,result.scene.coordinates.localOriginArcsec);
- const anchorPoints = result.scene.stars.map(star => ({id:star.id,positionUnits:reflectNebulaPoint(star.positionUnits),
+ const anchorPoints = result.scene.stars.map(star => ({id:star.id,positionUnits:reflectNebulaPoint(star.positionUnits,result.scene.frame),
colorCss:`#${star.rgb.map(n=>n.toString(16).padStart(2,'0')).join('')}`,opacity:star.alpha,sizePx:star.widthPx??1,
...(star.diameterUnits === undefined?{}:{diameterUnits:star.diameterUnits})}));
for (const lens of result.scene.lenses) {
const source = result.sources.find(source => source.id === lens.id)!;
const points = result.scene.stars.map(star => {
const material = star.materials?.[lens.id] ?? star;
- return { id:star.id,positionUnits:reflectNebulaPoint(star.positionUnits),
+ return { id:star.id,positionUnits:reflectNebulaPoint(star.positionUnits,result.scene.frame),
colorCss:`#${material.rgb.map(n=>n.toString(16).padStart(2,'0')).join('')}`,
opacity:material.alpha,sizePx:star.widthPx??1,...(material.diameterUnits === undefined?{}:{diameterUnits:material.diameterUnits}) };
});
@@ -246,10 +251,11 @@ export async function prepareNebulaObject(root: string, directory: string, ifMis
const data = validatePreparedVolumeLenses({schema:'cssearth-volume-lenses@1',id:recipe.id,defaultLens:recipe.defaultLens,
framingRadiusUnits:recipe.framingRadiusUnits,contextVisibility:'independent',starsEnabled:lenses[0]!.stars.points.length>0,
...(recipe.attachedTo === undefined ? {} : {attachedTo:recipe.attachedTo}),lenses});
+ const renderElements = assertCompilerDeliveryElementBudget(compilerSampling, data);
const envelope = json({schema:'cssearth-prepared-object@1',id:recipe.id,type:'volume-lens-bank',format:'cssearth-volume-lenses@1',data});
await put(resolve(staging,'lenses.json'),envelope);
await put(resolve(staging,'delivery.json'),json({schema:'cssearth-nebula-delivery-receipt@1',recipeSha256:sha256(recipeBytes),implementationSha256,sourceResult,
- acceptedLabResult:recipe.acceptedLabResult,...(fieldStars ? {fieldStars} : {}),
+ acceptedLabResult:recipe.acceptedLabResult,...(fieldStars ? {fieldStars} : {}), ...(renderElements ? { renderElements } : {}),
lenses:lenses.map(l=>({id:l.id,stars:l.stars.points.length,leaves:l.volume.resources.length}))}));
// Install complete generated files only. Authored source inputs stay untouched.
await mkdir(resolve(directory,'prepared'),{recursive:true});
diff --git a/tools/objects/telescopes/exploration.test.mts b/tools/objects/telescopes/exploration.test.mts
index 4ba110d904..22bbd12484 100644
--- a/tools/objects/telescopes/exploration.test.mts
+++ b/tools/objects/telescopes/exploration.test.mts
@@ -42,7 +42,7 @@ test('unknown targets return suggestions without exposing provider results', ()
});
test('explicit family filters separate known mismatches from selectable choices',()=>{
- const source={id:'image',target:'sun',telescope:'Fixture',mode:'camera',kind:'image' as const,archiveProductId:'archive-image',decoder:'fits-image' as const,files:[],identity:{OBJECT:'SUN'},units:'counts',meaning:'fixture',citation:'https://example.test/',limitations:[],qualified:false};
+ const source={id:'image',target:'sun',telescope:'Fixture',mode:'camera',kind:'image' as const,archiveProductId:'archive-image',decoder:'fits-image' as const,files:[],identity:{OBJECT:'SUN'},units:'counts',meaning:'fixture',citation:'https://example.test/',limitations:[],qualified:false,receipt:'image.receipt'};
const answer=explorationAnswer({target:'sun',family:'F16'},{ledgers:[],capabilities:[],targetCatalogue:[{id:'sun',name:'Sun',aliases:[]}],targetAssociations:[],bodyMaps:[],sourceProducts:[source],qualifiedProducts:[]});
assert.deepEqual(answer.choices,[]);assert.deepEqual(answer.unresolved,[]);assert.equal(answer.unsupported.length,1);assert.match(answer.unsupported[0]!.reason,/F01, not F16/u);
});
diff --git a/tools/objects/telescopes/families/f14-healpix.test.mts b/tools/objects/telescopes/families/f14-healpix.test.mts
index 1ff4fffd5d..19a81eba97 100644
--- a/tools/objects/telescopes/families/f14-healpix.test.mts
+++ b/tools/objects/telescopes/families/f14-healpix.test.mts
@@ -1,5 +1,5 @@
import assert from'node:assert/strict';import{test}from'node:test';import{copyFile,mkdtemp,readFile,rm,writeFile}from'node:fs/promises';import{tmpdir}from'node:os';import{resolve}from'node:path';import{member}from'./common.mts';import{describeHealpix,inspectHealpix,selectHealpix,validateHealpixMap,type HealpixMap}from'./f14-healpix.mts';import{pinFile,readProductRecord}from'../../product-record.mts';import{executeFamilyOperation}from'../family-operation.mts';
const map=(ordering:'RING'|'NESTED',semantics:'probability-mass'|'density'):HealpixMap=>({nside:1,ordering,frame:'C',quantity:'PROB',...(semantics==='density'?{unit:'1 / sr'}:{}),semantics,pixelAreaSteradians:Math.PI/3,pixels:12,astropy:'7.2.0',astropyHealpix:'1.1.3'});
-test('F14 retains native RING versus NESTED ordering and probability-mass versus density semantics',()=>{const nested=describeHealpix({id:'nested',member:member('map','bayestar.fits.gz','science',Buffer.from('map')),map:map('NESTED','probability-mass'),producingRecord:'upstream fixture'}),ring=describeHealpix({id:'ring',member:member('map','ring.fits','science',Buffer.from('map')),map:map('RING','density'),producingRecord:'adverse fixture'});assert.match(nested.components[0]!.axes[0]!.coordinates.convention,/NESTED/u);assert.match(nested.components[0]!.quantity.semantics,/probability-mass/u);assert.equal(nested.components[0]!.calibration.state,'unknown');assert.equal(nested.dataset.acquisition.kind,'local-import');assert.match(ring.components[0]!.axes[0]!.coordinates.convention,/RING/u);assert.match(ring.components[0]!.quantity.semantics,/density/u);});
+test('F14 retains native RING versus NESTED ordering and probability-mass versus density semantics',()=>{const nested=describeHealpix({id:'nested',member:member('map','bayestar.fits.gz','science',Buffer.from('map')),map:map('NESTED','probability-mass'),producingRecord:'upstream fixture'}),ring=describeHealpix({id:'ring',member:member('map','ring.fits','science',Buffer.from('map')),map:map('RING','density'),producingRecord:'adverse fixture'});const nestedCoordinates=nested.components[0]!.axes[0]!.coordinates;assert.ok(nestedCoordinates.kind==='native');assert.match(nestedCoordinates.convention,/NESTED/u);assert.match(nested.components[0]!.quantity.semantics,/probability-mass/u);assert.equal(nested.components[0]!.calibration.state,'unknown');assert.equal(nested.dataset.acquisition.kind,'local-import');const ringCoordinates=ring.components[0]!.axes[0]!.coordinates;assert.ok(ringCoordinates.kind==='native');assert.match(ringCoordinates.convention,/RING/u);assert.match(ring.components[0]!.quantity.semantics,/density/u);});
test('F14 rejects invalid pixel area and ambiguous quantity semantics',()=>{assert.throws(()=>validateHealpixMap({...map('RING','density'),pixelAreaSteradians:1}),/pixel area/u);assert.throws(()=>validateHealpixMap({...map('RING','density'),semantics:'mass' as any}),/semantics/u);assert.throws(()=>validateHealpixMap({...map('RING','density'),unit:undefined}),/steradian/u);});
test('F14 runs the pinned Bayestar IMPLICIT vector column through public selection and product readback',async()=>{const root=resolve(import.meta.dirname,'../../../..'),source=resolve(root,'tests/fixtures/telescope-families/f14-healpix/bayestar.fits.gz'),work=await mkdtemp(resolve(tmpdir(),'f14-healpix-'));try{const file=resolve(work,'bayestar.fits.gz');await copyFile(source,file);const pin={path:file,...await pinFile(file)},inspection=await inspectHealpix(pin);assert.equal(inspection.nside,512);assert.equal(inspection.ordering,'NESTED');assert.equal(inspection.pixels,3145728);assert.equal(inspection.astropyHealpix,'1.1.3');const direct=await selectHealpix(pin,{pixels:[0,1,3145727],components:['PROB']});assert.deepEqual(direct.selection.pixels,[0,1,3145727]);assert.equal(direct.values.PROB.length,3);const descriptor=describeHealpix({id:'bayestar',member:member('map','bayestar.fits.gz','science',await readFile(file),'application/fits'),map:inspection,producingRecord:'pinned reproject Bayestar fixture'}),path=resolve(work,'descriptor.json');await writeFile(path,JSON.stringify(descriptor));const result=await executeFamilyOperation(path,{operationId:'healpix-select',pixels:[0,1,3145727],components:['PROB']},resolve(work,'out')),record=await readProductRecord(result.record),reopened=JSON.parse(await readFile(result.product,'utf8'));assert.deepEqual(reopened.selection.pixels,[0,1,3145727]);assert.equal(record?.outputs[0]?.path,'healpix.json');assert.equal(record?.software.some(item=>item.name==='astropy-healpix'&&item.version==='1.1.3'),true);}finally{await rm(work,{recursive:true,force:true});}});
diff --git a/tools/objects/telescopes/families/f16-spatial-physical.mts b/tools/objects/telescopes/families/f16-spatial-physical.mts
index 59b43f75ae..f0802dd642 100644
--- a/tools/objects/telescopes/families/f16-spatial-physical.mts
+++ b/tools/objects/telescopes/families/f16-spatial-physical.mts
@@ -1,5 +1,5 @@
import{readFile}from'node:fs/promises';import{basename}from'node:path';import{requireRecord,requireString}from'../../../source-values.mts';import{inspectSpatialObject}from'../spatial-handoff.mts';import type{FamilyHandler,FamilyOperation}from'../family-handlers.mts';import type{ProductDescriptor}from'../product-descriptor.mts';import{descriptor,member,stable}from'./common.mts';
export async function describeSpatialPhysicalObject(objectPath:string,producingRecord=objectPath):Promise{const bytes=await readFile(objectPath),raw=requireRecord(JSON.parse(bytes.toString()),'physical object'),inspected=await inspectSpatialObject(objectPath),target=requireString(inspected.target),frame=requireRecord(inspected.frame,'physical frame'),bounds=requireRecord(frame.boundsUnits,'physical bounds'),objectMember=member('object',objectPath,'science',bytes,'application/json'),kind=inspected.kind,bank=kind==='volume-lens-bank'?inspected:undefined,provenance=inspected.provenance===undefined?'Top-level provenance is absent; per-lens provenance remains pinned inside the prepared closure.':JSON.stringify(inspected.provenance);return descriptor({schema:'cssearth-telescope-product-descriptor@1',dataset:{id:stable(requireString(raw.id),'physical object id'),target,acquisition:{kind:'repository',identity:objectPath},producingRecord,sourceClassifications:[{term:kind==='points'?'physical point field':kind==='volume-lens-bank'?'attached physical volume lens bank':'physical density volume',vocabulary:'cssearth-object@1',version:'1',status:'source'}],families:['F16'],profiles:[{handlerId:'f16-spatial-physical',profileId:'existing-physical-object@1'}]},members:[objectMember],components:[{id:kind,name:`${target} ${kind}`,families:['F16'],locations:[{memberId:'object',path:basename(objectPath)}],representation:{kind:'physical-field',topology:kind==='points'?'points':'grid',samples:null},axes:[],columns:[],quantity:{name:kind==='points'?'physical point samples':kind==='volume-lens-bank'?'selectable density/emission volumes':'density/emission volume',semantics:'Existing prepared physical object; no depth is inferred from an angular or spectral observation.'},calibration:{state:'model-derived',basis:[`Validated by the existing ${kind} loader across ${inspected.inputs} pinned inputs.`,provenance]},flags:[],frame:{kind:'physical',name:requireString(frame.referenceFrame),...(frame.epochJdTt===undefined?{}:{epoch:`JD_TT ${String(frame.epochJdTt)}`})},dependencyIds:[]}],dependencies:[],issues:[{scope:'component',identity:kind,state:'unknown',reason:`Portable export retains prepared resources and source recipe${bank?`, attachment to ${String(bank.attachedTo??'no body')}, default lens ${String(bank.defaultLens)}, and lens identities ${bank.lenses.join(', ')}`:''}; raw source datasets are referenced rather than bundled. Bounds: ${JSON.stringify(bounds)}.`}]});}
export function selectSpatialComponents(product:ProductDescriptor,componentIds:readonly string[]){if(!componentIds.length||componentIds.some(id=>!id))throw new TypeError('Physical component selection needs exact component IDs.');const selected=product.components.filter(component=>componentIds.includes(component.id));if(selected.length!==new Set(componentIds).size||selected.some(component=>!component.families.includes('F16')))throw new TypeError('Physical component selection references an absent or non-F16 component.');return selected;}
-const operations=(component:any):FamilyOperation[]=>{const kind:'points'|'volume'|'volume-lens-bank'=component.id==='volume-lens-bank'?'volume-lens-bank':component.representation.topology==='points'?'points':'volume';return[{id:'spatial-inspect',label:'Inspect existing physical object',owner:'inspectSpatialObject',module:'tools/objects/telescopes/spatial-handoff.mts',fixed:{}},{id:'spatial-select-components',label:'Select physical components',owner:'selectSpatialComponents',module:'tools/objects/telescopes/families/f16-spatial-physical.mts',fixed:{}},{id:`spatial-export-${kind}`,label:`Export physical ${kind}`,owner:'exportSpatialObject',module:'tools/objects/telescopes/spatial-handoff.mts',fixed:{kind}}].map(op=>({id:op.id,label:op.label,handlerId:'f16-spatial-physical',componentId:component.id,owner:{module:op.module,export:op.owner},available:true,reason:'The existing physical package and its prepared closure are validated by the application loader.',fixedArguments:op.fixed,parameters:[{id:'input',option:'--input',kind:'input-path',required:true,description:'Existing physical object.json.'},...(op.owner==='selectSpatialComponents'?[{id:'components',option:'--components',kind:'input-path' as const,required:true,description:'Exact physical component IDs.'}]:[]),{id:'out',option:'--out',kind:'output-directory' as const,required:true,description:'New output directory.'}],limitations:['A spectral cube or image is never treated as a physical depth reconstruction.']}));};
+const operations=(component:any):FamilyOperation[]=>{const kind:'points'|'volume'|'volume-lens-bank'=component.id==='volume-lens-bank'?'volume-lens-bank':component.representation.topology==='points'?'points':'volume';const definitions:readonly {id:string;label:string;owner:string;module:string;fixed:FamilyOperation['fixedArguments']}[]=[{id:'spatial-inspect',label:'Inspect existing physical object',owner:'inspectSpatialObject',module:'tools/objects/telescopes/spatial-handoff.mts',fixed:{}},{id:'spatial-select-components',label:'Select physical components',owner:'selectSpatialComponents',module:'tools/objects/telescopes/families/f16-spatial-physical.mts',fixed:{}},{id:`spatial-export-${kind}`,label:`Export physical ${kind}`,owner:'exportSpatialObject',module:'tools/objects/telescopes/spatial-handoff.mts',fixed:{kind}}];return definitions.map(op=>({id:op.id,label:op.label,handlerId:'f16-spatial-physical',componentId:component.id,owner:{module:op.module,export:op.owner},available:true,reason:'The existing physical package and its prepared closure are validated by the application loader.',fixedArguments:op.fixed,parameters:[{id:'input',option:'--input',kind:'input-path',required:true,description:'Existing physical object.json.'},...(op.owner==='selectSpatialComponents'?[{id:'components',option:'--components',kind:'input-path' as const,required:true,description:'Exact physical component IDs.'}]:[]),{id:'out',option:'--out',kind:'output-directory' as const,required:true,description:'New output directory.'}],limitations:['A spectral cube or image is never treated as a physical depth reconstruction.']}));};
export const F16_SPATIAL_PHYSICAL_HANDLER:FamilyHandler={id:'f16-spatial-physical',families:['F16'],profiles:[{id:'existing-physical-object@1',format:'cssearth-object@1 point-field, density-volume or volume-lens-bank with verified prepared closure',version:'1',families:['F16'],publicBaseline:true,evidence:[{path:'tools/objects/telescopes/output-handoffs.test.mts',establishes:'Real physical point-field and body-attached volume-lens-bank inspection/export use the existing loaders and preserve frame/provenance.',status:'complete'},{path:'src/objects/milky-way/object.json',establishes:'Real pinned density-volume closure validates through the same spatial owner.',status:'complete'}]}],recognizes:members=>{for(const item of members){try{const value=JSON.parse(Buffer.from(item.prefix).toString()) as any;if(value.schema==='cssearth-object@1'&&(value.type==='point-field'||value.type==='density-volume'||value.type==='volume-lens-bank'))return['existing-physical-object@1'];}catch{}}return[];},operations:d=>d.components.filter(c=>c.families.includes('F16')).flatMap(operations)};
diff --git a/tools/objects/telescopes/family-examples.test.mts b/tools/objects/telescopes/family-examples.test.mts
index 89391141b9..821040c514 100644
--- a/tools/objects/telescopes/family-examples.test.mts
+++ b/tools/objects/telescopes/family-examples.test.mts
@@ -6,7 +6,7 @@ import test from 'node:test';
import {executableFamilyOperations} from './family-operation.mts';
import {parseProductDescriptor} from './product-descriptor.mts';
-const root=resolve(import.meta.dirname,'../../..'),base=resolve(root,'docs/telescope-family-examples');
+const root=resolve(import.meta.dirname,'../../..'),base=resolve(root,'tests/fixtures/telescope-family-examples');
const assigned=['F01','F02','F03','F04','F05','F06','F07','F08','F09','F10','F11','F12','F13','F14','F15','F16','F17','F18'];
const sha256=(bytes:Buffer)=>createHash('sha256').update(bytes).digest('hex');
@@ -30,6 +30,7 @@ test('checked family examples contain exactly one pinned representative per assi
if(example.family==='F01')assert.equal(tokens[1],'export');
else{
assert.equal(tokens[1],'family-run');const descriptorPath=tokens[2],operationId=tokens[3],descriptor=parseProductDescriptor(JSON.parse(await readFile(resolve(root,descriptorPath),'utf8')));
+ if(descriptor.dataset.acquisition.identity.startsWith('../'))assert.equal((await stat(resolve(root,descriptorPath,'..',descriptor.dataset.acquisition.identity))).isFile(),true,`${example.family} acquisition identity`);
assert.deepEqual(descriptor.dataset.families,[example.family]);assert.ok(executableFamilyOperations(descriptor).some(operation=>operation.id===operationId&&operation.available),`${example.family} ${operationId} executable`);
for(const member of descriptor.members){const file=resolve(resolve(root,descriptorPath),'..',member.path),bytes=await readFile(file);assert.equal(bytes.length,member.bytes);assert.equal(sha256(bytes),member.sha256);}
const params=tokens.indexOf('--params');if(params>=0){const value=JSON.parse(await readFile(resolve(root,tokens[params+1]!),'utf8'));assert.equal(value.operationId,operationId);}
diff --git a/tools/objects/telescopes/family-operation.mts b/tools/objects/telescopes/family-operation.mts
index a596f527c7..1022506f60 100644
--- a/tools/objects/telescopes/family-operation.mts
+++ b/tools/objects/telescopes/family-operation.mts
@@ -50,7 +50,8 @@ type FamilyOperationArguments=
|{readonly operationId:'count-image-preview';readonly image:{readonly x:readonly[number,number];readonly y:readonly[number,number];readonly bins:readonly[number,number];readonly selection?:EventSelection}}
|{readonly operationId:'energy-histogram-preview';readonly histogram:{readonly edgesEv:readonly number[];readonly selection?:EventSelection}}
|{readonly operationId:'light-curve-preview';readonly lightCurve:{readonly range:readonly[number,number];readonly binWidthSeconds:number;readonly selection?:EventSelection}}
- |{readonly operationId:'slit-scan-inspect'|'dynamic-spectrum-inspect'|'polarimetry-inspect'|'polarimetry-export'}
+ |{readonly operationId:'slit-scan-inspect'|'polarimetry-inspect'|'polarimetry-export'}
+ |{readonly operationId:'dynamic-spectrum-inspect'}
|{readonly operationId:'stokes-inspect'}
|{readonly operationId:'stokes-image'|'stokes-series';readonly component:'I'|'Q'|'U'|'V';readonly slice:Readonly>}
|{readonly operationId:'stokes-spectrum';readonly component:'I'|'Q'|'U'|'V';readonly slice:Readonly>;readonly x:number;readonly y:number}
diff --git a/tools/objects/telescopes/family-operation.test.mts b/tools/objects/telescopes/family-operation.test.mts
index e71b667cc5..b6f118447e 100644
--- a/tools/objects/telescopes/family-operation.test.mts
+++ b/tools/objects/telescopes/family-operation.test.mts
@@ -62,7 +62,7 @@ test('F07 public selection runs the pinned cdflib owner and writes native coordi
test('F11 public dispatcher delegates bounded real UVFITS work to pyuvdata and attaches its package-owned preview',async()=>{const work=await temporary();try{const source=resolve(root,'tests/fixtures/telescope-families/f11-vla-uvfits/day2_TDEM0003_10s_norx_1src_1spw.uvfits'),bytes=await readFile(source),descriptor=describeUvfitsVisibility({id:'vla-day2-tdem0003',producingRecord:'RASG datasets v0.0.5 BSD-2-Clause',member:member('uvfits',source,'science',bytes,'application/fits'),inspection:{telescope:'EVLA',target:'J1008+0730',rows:1360,channels:64,polarizations:[-1,-2,-3,-4],samples:348160,frequencyHz:{first:36304541952.42,increment:125000}}}),path=await save(work,descriptor),selection={field:'J1008+0730',timeStartJulianDate:2455312.64022,timeEndJulianDate:2455312.64024,antenna1:4,antenna2:8,rowOffset:0,rowCount:1,channelStart:0,channelCount:2,polarization:-1},result=await executeFamilyOperation(path,{operationId:'uvfits-amplitude-phase-diagnostics',selection},resolve(work,'diagnostic')),value=JSON.parse(await readFile(result.product,'utf8')),record=await readProductRecord(result.record);assert.equal(value.pyuvdata,'3.2.4');assert.equal(value.amplitudePhase.unflaggedSamples,2);assert.equal(record?.software.some(item=>item.name==='pyuvdata'&&item.version==='3.2.4'),true);const exported=await executeFamilyOperation(path,{operationId:'uvfits-visibility-export',selection},resolve(work,'export')),exportRecord=await readProductRecord(exported.record);assert.deepEqual(exportRecord?.outputs.map(item=>item.path),['visibilities.json','preview.png','preview.svg']);assert.equal((await readFile(resolve(exported.directory,'preview.png'))).subarray(1,4).toString(),'PNG');}finally{await rm(work,{recursive:true,force:true});}});
test('remaining family baseline operations execute through the public dispatcher on pinned examples',async()=>{const work=await temporary();try{
- const descriptor=(family:string)=>resolve(root,'docs/telescope-family-examples/descriptors',`${family}.json`),run=async(family:string,parameters:any,name:string)=>{const path=descriptor(family),value=JSON.parse(await readFile(path,'utf8')),matches=executableFamilyOperations(value).filter(operation=>operation.id===parameters.operationId&&operation.available);return executeFamilyOperation(path,{...parameters,...(matches.length>1?{componentId:matches[0]!.componentId}:{})},resolve(work,name));};
+ const descriptor=(family:string)=>resolve(root,'tests/fixtures/telescope-family-examples/descriptors',`${family}.json`),run=async(family:string,parameters:any,name:string)=>{const path=descriptor(family),value=JSON.parse(await readFile(path,'utf8')),matches=executableFamilyOperations(value).filter(operation=>operation.id===parameters.operationId&&operation.available);return executeFamilyOperation(path,{...parameters,...(matches.length>1?{componentId:matches[0]!.componentId}:{})},resolve(work,name));};
const range=await readFile((await run('F03',{operationId:'spectrum-select-range',range:[350,355]},'spectrum-range')).product,'utf8');assert.match(range,/350/u);const chart=JSON.parse(await readFile((await run('F03',{operationId:'spectrum-chart-data'},'spectrum-chart')).product,'utf8'));assert.ok(chart.length>1);const preview=await run('F03',{operationId:'spectrum-preview'},'spectrum-preview');assert.equal((await readFile(preview.product)).subarray(1,4).toString(),'PNG');
const profile=JSON.parse(await readFile((await run('F04',{operationId:'slit-profile-table'},'slit-profile')).product,'utf8'));assert.equal(profile.length,4);const slit=JSON.parse(await readFile((await run('F04',{operationId:'slit-region-spectrum',directory:resolve(root,'tests/fixtures/telescope-families/f04-europa-stis'),frame:'od9l12010_x2d.fits',row:125},'slit-spectrum')).product,'utf8'));assert.equal(slit.row,125);assert.equal(slit.wavelengthAngstrom.length,slit.flux.length);
const sed=JSON.parse(await readFile((await run('F05',{operationId:'sed-plot-data'},'sed')).product,'utf8'));assert.equal(sed[0].band,'V');
diff --git a/tools/objects/telescopes/local-import.test.mts b/tools/objects/telescopes/local-import.test.mts
index dd48c39707..cedf43f6fe 100644
--- a/tools/objects/telescopes/local-import.test.mts
+++ b/tools/objects/telescopes/local-import.test.mts
@@ -57,6 +57,6 @@ test('local HEALPix import reuses its Astropy-owned descriptor and operations',a
const root=await mkdtemp(resolve(tmpdir(),'telescope-import-healpix-')),source=resolve(import.meta.dirname,'../../../tests/fixtures/telescope-families/f14-healpix/bayestar.fits.gz'),copy=resolve(root,'bayestar.fits.gz'),out=resolve(root,'imported');
try{
await copyFile(source,copy);const result=await importLocalArtifact({schema:'cssearth-telescope-local-import-spec@1',datasetId:'bayestar-import',sources:[{path:copy,role:'science'}],declarations:{familyHints:['F14']},limits:{maxMembers:1,maxBytes:200000,maxFileBytes:200000}},out),inspected=await listArtifactOutputs(result.manifest);
- assert.equal(result.value.descriptor?.path,'descriptor.json');assert.ok(inspected.familyOperations?.some(operation=>operation.id==='healpix-select'&&operation.available));
+ assert.equal(result.value.descriptor?.path,'descriptor.json');assert.ok('familyOperations' in inspected&&inspected.familyOperations?.some(operation=>operation.id==='healpix-select'&&operation.available));
}finally{await rm(root,{recursive:true,force:true});}
});
diff --git a/tools/objects/telescopes/session.test.mts b/tools/objects/telescopes/session.test.mts
index c1627cb85c..c678d00468 100644
--- a/tools/objects/telescopes/session.test.mts
+++ b/tools/objects/telescopes/session.test.mts
@@ -117,6 +117,7 @@ test('saved choices qualify, export complete dependencies, reassess and reuse wi
const altered = { ...saved, choices: saved.choices.map(c => ({ ...c, configuration: { kind: 'unknown' }, product: '/untrusted/file' })) };
await writeFile(resolve(out, 'query.json'), JSON.stringify(altered));
const result = await getSession(f.root, out, 1, () => {}, f.api);
+ assert.ok('satisfaction' in result);
assert.equal(result.satisfaction.status, 'unresolved'); assert.equal(f.qualifications(), 1);
const delivery = JSON.parse(await readFile(result.resultPath, 'utf8'));
assert.ok(delivery.files.some((f: { path: string }) => f.path.endsWith('core.lbl')));
diff --git a/tools/objects/telescopes/spatial-handoff.mts b/tools/objects/telescopes/spatial-handoff.mts
index 603e3e979d..cd9431ca7f 100644
--- a/tools/objects/telescopes/spatial-handoff.mts
+++ b/tools/objects/telescopes/spatial-handoff.mts
@@ -9,6 +9,10 @@ import { sha256 } from '../../../src/platform/sha256.mts';
import { writeProductRecord,type ProductInput } from '../product-record.mts';
export type SpatialKind='points'|'volume'|'volume-lens-bank';
+type SpatialPayload =
+ | {kind:'points';payload:Awaited>}
+ | {kind:'volume';payload:Awaited>}
+ | {kind:'volume-lens-bank';payload:Awaited>};
const workspaceRoot=resolve(import.meta.dirname,'../../..');
async function validateSpatialObject(objectPath:string,expected:SpatialKind|undefined){
@@ -45,32 +49,37 @@ async function validateSpatialObject(objectPath:string,expected:SpatialKind|unde
try{
const loader: {loadPreparedCssPointField?:typeof import('../../../src/renderers/css/stars/loader.ts').loadPreparedCssPointField;loadPreparedCssVolume?:typeof import('../../../src/renderers/css/volume/loader.ts').loadPreparedCssVolume;loadPreparedVolumeLenses?:typeof import('../../../src/renderers/css/volume/prepared-volume-lenses.ts').loadPreparedVolumeLenses}=await import(`${pathToFileURL(moduleFile).href}?${randomUUID()}`);
const transport={read:async(path:string)=>Uint8Array.from(await read(path)).buffer};
- const payload=kind==='points'?await loader.loadPreparedCssPointField!(descriptor,transport):kind==='volume'?await loader.loadPreparedCssVolume!(descriptor,transport):await loader.loadPreparedVolumeLenses!(descriptor,transport);
+ const value:SpatialPayload=kind==='points'?{kind,payload:await loader.loadPreparedCssPointField!(descriptor,transport)}
+ :kind==='volume'?{kind,payload:await loader.loadPreparedCssVolume!(descriptor,transport)}
+ :{kind,payload:await loader.loadPreparedVolumeLenses!(descriptor,transport)};
const manifestPath=requireString(prepared.url);
- const resources=kind==='volume-lens-bank'?payload.lenses.flatMap(lens=>lens.volume.resources):payload.resources;
+ const resources=value.kind==='volume-lens-bank'?value.payload.lenses.flatMap(lens=>lens.volume.resources):value.payload.resources;
for(const resource of resources){const path=relative(root,resolve(root,dirname(manifestPath),resource.path)),content=await read(path);if(content.length!==resource.bytes||sha256(content)!==resource.sha256)throw new Error(`Spatial resource pin mismatch: ${resource.path}`);}
- return {source,root,bytes,descriptor,kind,checked,payload,compiled};
+ return {source,root,bytes,descriptor,checked,compiled,...value};
}finally{await rm(scratch,{recursive:true,force:true});}
}
export async function inspectSpatialObject(objectPath:string){
- const value=await validateSpatialObject(objectPath,undefined),bank=value.kind==='volume-lens-bank'?value.payload:undefined;
- return {kind:value.kind,target:value.descriptor.id,frame:bank?requireRecord(requireRecord(value.descriptor.properties).frame,'physical frame'):value.payload.frame,provenance:value.payload.provenance,inputs:value.checked.size+1,
- ...(bank?{attachedTo:bank.attachedTo??null,defaultLens:bank.defaultLens,lenses:bank.lenses.map((lens:{id:string})=>lens.id)}:{})};
+ const value=await validateSpatialObject(objectPath,undefined);
+ if(value.kind==='volume-lens-bank')return {kind:value.kind,target:value.descriptor.id,
+ frame:requireRecord(requireRecord(value.descriptor.properties).frame,'physical frame'),provenance:value.payload.provenance,inputs:value.checked.size+1,
+ attachedTo:value.payload.attachedTo??null,defaultLens:value.payload.defaultLens,lenses:value.payload.lenses.map(lens=>lens.id)};
+ return {kind:value.kind,target:value.descriptor.id,frame:value.payload.frame,provenance:value.payload.provenance,inputs:value.checked.size+1};
}
export async function exportSpatialObject(objectPath:string,kind:SpatialKind,outputDirectory:string){
const destination=resolve(outputDirectory),staging=`${destination}.${randomUUID()}.partial`;
await mkdir(dirname(destination),{recursive:true});await mkdir(destination);await mkdir(staging);
try{
- const {source,bytes,descriptor,checked,payload,compiled}=await validateSpatialObject(objectPath,kind);
+ const value=await validateSpatialObject(objectPath,kind);
+ const {source,bytes,descriptor,checked,payload,compiled}=value;
const outputs=[{path:'object.json',file:resolve(staging,'object.json')}];await writeFile(outputs[0].file,bytes);
for(const [path,item] of checked){const file=resolve(staging,path);await mkdir(dirname(file),{recursive:true});await writeFile(file,item.bytes);outputs.push({path,file});}
for(const item of checked.values())if(sha256(await readFile(item.pin.identity))!==item.pin.sha256)throw new Error('Spatial source changed during export');
if(sha256(await readFile(source))!==sha256(bytes))throw new Error('Spatial descriptor changed during export');
const implementation=sha256(Buffer.concat([await readFile(new URL('spatial-handoff.mts',import.meta.url)),...await Promise.all(Object.keys(compiled.metafile.inputs).sort().map(file=>readFile(file)))]));
- const frame=kind==='volume-lens-bank'?requireRecord(requireRecord(descriptor.properties).frame,'physical frame'):payload.frame;
- await writeProductRecord(resolve(staging,'output.product.json'),{telescope:'css.earth physical source package',stage:'telescope-spatial-handoff',inputs:[{role:'physical descriptor',identity:source,sha256:sha256(bytes),bytes:bytes.length},...Array.from(checked.values(),item=>item.pin)],parameters:{kind,target:descriptor.id,frame,provenance:payload.provenance,...(kind==='volume-lens-bank'?{attachedTo:payload.attachedTo??null,defaultLens:payload.defaultLens,lenses:payload.lenses.map((lens:{id:string})=>lens.id)}:{}),interpretation:'Existing prepared physical object; no new depth inference, reconstruction or qualification of an observation.',scope:'Portable renderer resources, source recipe and credits; raw source datasets are referenced, not bundled.'},software:[{name:'css.earth existing physical object loader',version:implementation}]},outputs);
+ const frame=value.kind==='volume-lens-bank'?requireRecord(requireRecord(descriptor.properties).frame,'physical frame'):value.payload.frame;
+ await writeProductRecord(resolve(staging,'output.product.json'),{telescope:'css.earth physical source package',stage:'telescope-spatial-handoff',inputs:[{role:'physical descriptor',identity:source,sha256:sha256(bytes),bytes:bytes.length},...Array.from(checked.values(),item=>item.pin)],parameters:{kind,target:descriptor.id,frame,provenance:payload.provenance,...(value.kind==='volume-lens-bank'?{attachedTo:value.payload.attachedTo??null,defaultLens:value.payload.defaultLens,lenses:value.payload.lenses.map(lens=>lens.id)}:{}),interpretation:'Existing prepared physical object; no new depth inference, reconstruction or qualification of an observation.',scope:'Portable renderer resources, source recipe and credits; raw source datasets are referenced, not bundled.'},software:[{name:'css.earth existing physical object loader',version:implementation}]},outputs);
await rmdir(destination);await rename(staging,destination);
return {directory:destination,object:resolve(destination,'object.json'),receipt:resolve(destination,'output.product.json'),kind};
}catch(error){await rm(staging,{recursive:true,force:true});await rmdir(destination).catch(()=>{});throw error;}
diff --git a/tools/objects/telescopes/vo/session.test.mts b/tools/objects/telescopes/vo/session.test.mts
index b6b36d41db..ae25fb1b79 100644
--- a/tools/objects/telescopes/vo/session.test.mts
+++ b/tools/objects/telescopes/vo/session.test.mts
@@ -102,7 +102,9 @@ test('two saved SODA subsets of one parent keep separate acquisition, qualificat
const api: SessionServices = { load: async () => load(root, discoveredRequest), loadRequest: load, qualify: async (_root, qualification) => {
const configuration = qualification.configuration;
if (configuration.kind !== 'archive-acquisition') throw new Error('Expected an archive subset qualification.');
- const spec = (await planFor(configuration.request)).products.find(product => product.key === configuration.key);
+ const request = configuration.request;
+ assert.ok(request.wavelengthMicrometres, 'This scientific subset fixture retains its requested wavelengths.');
+ const spec = (await planFor({ ...request, wavelengthMicrometres: request.wavelengthMicrometres })).products.find(product => product.key === configuration.key);
if (!spec) throw new Error('Saved subset acquisition was not rediscovered.');
return qualifyVoProduct(root, spec);
} };
diff --git a/tools/prepare-navigation-destinations.mts b/tools/prepare-navigation-destinations.mts
index cb99bf8682..5bdbbeb4f7 100644
--- a/tools/prepare-navigation-destinations.mts
+++ b/tools/prepare-navigation-destinations.mts
@@ -26,7 +26,7 @@ export function prepareSceneDistance(descriptor: unknown) {
}
export function prepareFocusObject(object: PreparedCatalogObject, sceneHostId: string) {
- const classification = isPreparedCluster(object) ? 'galaxy-cluster' : isPreparedNebula(object) ? 'nebula' : 'galaxy';
+ const classification = isPreparedCluster(object) ? 'galaxy-cluster' : isPreparedNebula(object) ? object.kind : 'galaxy';
return definePreparedFocus({ kind: 'prepared-focus', id: object.id, focusId: object.id, name: object.name,
searchNames: [...new Set([object.id, object.name, ...object.aliases].flatMap(name => {
const normalized = normalizeDestinationQuery(name);
diff --git a/tools/prepare-volume-provenance.mts b/tools/prepare-volume-provenance.mts
index e02ad9c474..6d1785f094 100644
--- a/tools/prepare-volume-provenance.mts
+++ b/tools/prepare-volume-provenance.mts
@@ -176,6 +176,8 @@ export async function readPreparedVolumeProvenance({ root = process.cwd(), input
}
interface Options {
root?: string;
+ /** Prepare one volume without acquiring unrelated objects' preview sources. */
+ objectId?: string;
/** Repository-relative, tracked compiler inputs only. Downloads never enter source closure. */
input?: (path: string) => Promise;
/** Opt-in (default null/off): the real content-addressed mirror origin, named explicitly by a production caller.
@@ -236,12 +238,13 @@ export async function preparePreview(root: string, pin: Preview, input: (path: s
}
/** Recover portable lineage from source-owned byte pins without replaying the cloud compiler. */
-export async function prepareVolumeProvenance({ root = process.cwd(), input = path => readFile(resolve(root, path)), mirrorOrigin = null }: Options = {}): Promise {
+export async function prepareVolumeProvenance({ root = process.cwd(), objectId, input = path => readFile(resolve(root, path)), mirrorOrigin = null }: Options = {}): Promise {
+ if (objectId !== undefined) sourceId(objectId);
const results: PreparedVolumeProvenance[] = [];
const generatorBytes = await input(volumeProvenanceCompilerClosure[0]);
for (const path of volumeProvenanceCompilerClosure.slice(1)) await input(path);
const folders = await readdir(resolve(root, 'src/objects'), { withFileTypes: true });
- for (const folder of folders.filter(folder => folder.isDirectory()).sort((a, b) => a.name.localeCompare(b.name))) {
+ for (const folder of folders.filter(folder => folder.isDirectory() && (objectId === undefined || folder.name === objectId)).sort((a, b) => a.name.localeCompare(b.name))) {
const base = `src/objects/${folder.name}`, presentationPath = `${base}/source/presentation.json`;
const presentationBytes = await readFile(resolve(root, presentationPath)).catch((error: unknown) => { if (hasErrorCode(error, 'ENOENT')) return null; throw error; });
if (presentationBytes === null || sourceObject(json(presentationBytes)).schema !== 'cssearth-volume-presentation-source@1') continue;
@@ -356,6 +359,7 @@ export async function prepareVolumeProvenance({ root = process.cwd(), input = pa
results.push({ id: record.objectId, name: record.name, route: hostedBy?.route ?? `/sun/?focus=${record.objectId}`, base, controls, defaultLens: record.defaultLens, provenance, outputs,
...(hostedBy === undefined ? {} : { hostedBy }) });
}
+ if (objectId !== undefined && results.length !== 1) throw new TypeError(`No volume presentation for ${objectId}.`);
return results;
}
@@ -369,7 +373,11 @@ export async function writeVolumeProvenance(options: Options = {}) {
}
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
+ const args = process.argv.slice(2);
+ if (args.length > 1 || args.some(arg => !/^--object=[a-z][a-z0-9-]*$/.test(arg)))
+ throw new TypeError('Usage: prepare-volume-provenance [--object=].');
// The real CLI entry point: opts into the mirror explicitly (library code above defaults it off).
- const results = await writeVolumeProvenance({ mirrorOrigin: RUNTIME_ASSET_ORIGIN });
+ const results = await writeVolumeProvenance({ mirrorOrigin: RUNTIME_ASSET_ORIGIN,
+ ...(args[0] === undefined ? {} : { objectId: args[0].slice(9) }) });
console.log(`Prepared volume presentation and provenance: ${results.length} objects, ${results.reduce((sum, result) => sum + result.controls.length, 0)} lenses.`);
}
diff --git a/tools/prepare-volume-provenance.test.mts b/tools/prepare-volume-provenance.test.mts
index e0dc21bf26..9273de3c90 100644
--- a/tools/prepare-volume-provenance.test.mts
+++ b/tools/prepare-volume-provenance.test.mts
@@ -11,11 +11,24 @@ import { productSourceIds } from '../src/platform/object-provenance.mts';
import { prepareVolumeProvenance } from './prepare-volume-provenance.mts';
const root = resolve(import.meta.dirname, '..');
+test('one selected volume prepares without reading unrelated presentation inputs', async () => {
+ const seen: string[] = [];
+ const entries = await prepareVolumeProvenance({ root, objectId: 'omega-centauri', input: async path => {
+ if (path.endsWith('/source/presentation.json')) assert.equal(path, 'src/objects/omega-centauri/source/presentation.json');
+ seen.push(path);
+ return readFile(resolve(root, path));
+ } });
+ assert.deepEqual(entries.map(entry => [entry.id, entry.controls.length]), [['omega-centauri', 2]]);
+ assert.ok(seen.includes('src/objects/omega-centauri/source/delivery.json'));
+ assert.equal(entries[0]!.route, '/sun/?focus=omega-centauri');
+ await assert.rejects(prepareVolumeProvenance({ root, objectId: 'absent-test-volume' }), /No volume presentation/);
+});
+
test('all installed volume lenses retain real source-to-product edges', async () => {
const closure = new Set();
const entries = await prepareVolumeProvenance({ root, input: async path => { closure.add(path); return readFile(resolve(root, path)); } });
assert.deepEqual(entries.map(entry => [entry.id, entry.controls.length]), [
- ['betelgeuse-shell', 4], ['hd-181327-disc', 1], ['helix', 3], ['lmc', 3], ['m1', 6], ['m2-9', 1], ['m31', 1], ['m33', 1], ['m42', 2], ['m45', 5], ['m8', 3], ['smc', 5], ['sun-cor1-density', 1],
+ ['betelgeuse-shell', 4], ['hd-181327-disc', 1], ['helix', 3], ['lmc', 3], ['m1', 6], ['m2-9', 1], ['m31', 1], ['m33', 1], ['m42', 2], ['m45', 5], ['m8', 3], ['omega-centauri', 2], ['smc', 5], ['sun-cor1-density', 1],
]);
assert.equal(entries.find(entry => entry.id === 'm45')?.defaultLens, 'optical-composite');
assert.ok([...closure].every(path => !path.startsWith('.local/') && !path.endsWith('/prepared/lenses.json')));