Date: 2026-08-07 Status: additive engine, facade, REST, data, validation, and local Phase 7 artifact gates closed; publication and deployment remain separate Governing roadmap: PHYSICAL_HELIACAL_VISIBILITY_IMPLEMENTATION_PLAN.md
This document freezes the meanings, supported domain, failure law, data boundary, and additive public-contract shape for Moira’s physical heliacal-visibility project.
It is the implementation authority for the admitted opt-in physical assessment and event model. The existing legacy search remains the default public/facade/REST behavior; the additive physical contract does not silently reinterpret that compatibility surface. Phase 5 admitted the dedicated facade and REST transports, Phase 6 admitted two private numerical kernels, and Phase 7 binds the evidence, external resource, packaging, and release boundary.
The existing HeliacalEventKind enum is frozen with these six strings:
heliacal_risingheliacal_settingacronychal_risingacronychal_settingcosmic_risingcosmic_settingTheir current calculations, defaults, return shapes, None behavior, facade
behavior, and REST projection remain legacy contracts. The modernization must
not:
The current CRUMEY_2014_POINT_SOURCE single-epoch assessment also remains
unchanged. It is not silently widened from its admitted scotopic astronomical
domain into a twilight event criterion.
The first composite physical model identifier is:
clear_sky_naked_eye_point_source_v1
This is a first-class immutable model family, not a moving alias for “best available.” A policy preset may select it, but the preset must resolve to this exact identifier and expose every component identity.
The initial component identities are:
| Component | Frozen identifier | Role |
|---|---|---|
| Directional atmosphere | libradtran_2_0_6_mystic_spherical_v1 |
Offline-generated transmission and twilight radiance |
| Point-source detection | blackwell_crumey_full_range_point_source_v1 |
Detection threshold across the generated twilight luminance domain |
| Spectral response | cie_mes2_2010_v1 |
Declared photopic/scotopic interpolation, never an unnamed conversion |
| Observational lineage | tousey_koomen_twilight_1953_v1 |
Independent twilight comparison cases |
These identifiers have passed the Phase 1-3 engine gates, the Phase 5-6 transport and native gates, and the local Phase 7 offline artifact gate. They stay opt-in and do not become facade or REST defaults.
The additive direct-module enum is PhysicalVisibilityPhase with exactly these
four strings:
| Value | Within-day event | Across-day ownership |
|---|---|---|
morning_first_rising |
Opening of a visible interval connected to the target’s apparent rising before apparent sunrise | First qualifying morning after a non-qualifying morning |
morning_first_setting |
Closing of a visible interval connected to the target’s apparent setting before apparent sunrise | First qualifying morning after a non-qualifying morning |
evening_last_rising |
Opening of a visible interval connected to the target’s apparent rising after apparent sunset | Last qualifying evening before a non-qualifying evening |
evening_last_setting |
Closing of a visible interval connected to the target’s apparent setting after apparent sunset | Last qualifying evening before a non-qualifying evening |
This follows the four visible phases summarized from Ptolemy by Schironi: morning rising and setting are first appearances before sunrise; evening rising and setting are last appearances after sunset.
The words heliacal, acronychal, and cosmic are not used in this new enum.
Modern usage of those labels is not consistent enough to carry the exact
event law without the morning/evening, first/last, and rising/setting terms.
Legacy cosmic events remain legacy geometrical events. They are outside the new physical four-phase taxonomy.
The API receives longitude but not a civil timezone. It therefore must not invent a civil date.
The physical solver assigns samples to a deterministic local mean solar day:
observation_day_key = floor(jd_ut + 0.5 + longitude_deg / 360)
longitude_deg uses Moira’s existing east-positive convention.
The key identifies the candidate local observing day. Results also return the complete UT Julian dates, so callers with a civil-time authority can project the event themselves.
search_window_days limits candidate phase-day keys. The solver may inspect
one guard day before or after that interval solely to prove first/last
ownership. A guard day cannot be returned as the requested event unless it is
inside the caller’s candidate interval.
For each candidate day, the solver constructs the connected interval in which:
Morning windows end at apparent sunrise. Evening windows begin at apparent sunset. Apparent sunrise and sunset use the same refraction and horizon policy as the returned window receipt.
A day qualifies only when a non-empty visible interval is connected to the relevant apparent target rising or setting. An isolated visibility island elsewhere in the night does not qualify as that target phase.
The visibility margin is:
visibility_margin(t)
= limiting_magnitude(t) - conditioned_target_magnitude(t)
The boundary rule is:
not_visible -> visible;visible -> not_visible.If the horizon itself creates the transition, the threshold time may coincide with the apparent target rise or set. Otherwise the event time is the refined zero of the physical visibility margin inside the connected window.
The event receipt distinguishes visibility_margin and target_horizon
boundary sources. A margin-root residual is required only for a
visibility_margin boundary; it is not fabricated as zero for a horizon
boundary.
The solver must find every candidate crossing and must separately detect a tangent or near-zero interval. Returning the first sampled visible time is not an event solution.
The first event admission binds a source-controlled Lipschitz/zero-enclosure
certificate to the exact version 1.2 data pack. Same-sign intervals are
excluded only when the admitted rate bound proves they cannot contain zero;
otherwise they are recursively enclosed. A possible-zero interval without a
witness fails closed as crossing_completeness_not_certified. Dense sampling
alone is not called a completeness certificate.
A morning phase is returned only if:
An evening phase is returned only if:
Missing evidence on either comparison day is not treated as “not visible.” It makes first/last ownership not evaluable.
The first admission is:
binocular here means use of both unaided eyes, not binocular optical
equipment.
The observer protocol identifier is:
known_location_directed_averted_observation_v1
It assumes a continuous non-flickering target, natural pupils, a known target position, deliberate directed attention, and averted/peripheral fixation after adaptation to the immediate directional field. This is the task admitted by the CIE MES2 component; it is not a claim about central/foveal detection. Casual discovery, wide-field search, central fixation, flicker, and a population probability are different experiments and are not silently represented by an experience or confidence slider.
If the admitted threshold equations require a fixed field factor, the value and source equation are part of the component receipt. It is not a mutable proxy for observer skill.
For known_location_directed_averted_observation_v1, the admitted contract is
the fixed Crumey equation-53 notional value F = 2. Crumey’s F can combine
target, medium, laboratory-scaling, detection-practice, and personal effects;
it is not a separable generic observer variable. The physical policy therefore
exposes no experience or confidence slider. A different calibrated value
requires a separately versioned observer protocol with its own experimental
receipt and validity domain.
The first planetary candidates are Mercury, Venus, Mars, Jupiter, and Saturn. A planet enters the public model only after its dynamic visual magnitude and target spectral treatment carry source receipts and pass independent validation.
Those five candidates have passed the Phase 2 single-epoch engine gate. Their planetary response profiles are pack-owned and cannot be replaced by caller-supplied weights. Phase angle and, for Saturn, effective ring sub-latitude must remain inside the source-owned profile domain or the assessment fails closed.
A fixed star enters only when its identity, visual photometry, and spectral or
color transformation are source-identified and complete. An ambiguous catalog
color_index must not be guessed to be B-V, Gaia BP-RP, or another system.
The first Phase 3 event admission is deliberately narrower:
body_phase_not_admitted for physical event search because required
guard days can leave their source-owned phase-angle domains; andtarget_not_admitted.Sirius never enters the legacy native arcus accelerator. Its exact catalog identity, visual magnitude, source receipts, and response weights must agree with the external pack or evaluation fails closed.
Uranus, Neptune, minor planets, comets, novae, and other targets require separate target-admission evidence. They are not accepted merely because the ephemeris layer can calculate a position.
Phase 1 may narrow these limits after grid and error measurements. It may not widen them without amending this doctrine.
| Quantity | Hard outer envelope | Unit or convention |
|---|---|---|
| Spectral integration | 380 to 780 | nm |
| Solar-center altitude | -18 to 0 | geometric degrees |
| Target altitude used by radiative transfer | -1 to 45 | true geometric degrees |
| Relative solar azimuth | 0 to 180 | absolute degrees |
| Observer altitude | 0 to 5,000 | m above mean sea level |
| Surface pressure | 500 to 1,100 | hPa |
| Aerosol optical depth | 0 to 1 | AOD at 550 nm |
| Angstrom exponent | 0 to 2.5 | dimensionless |
| Total ozone column | 200 to 500 | Dobson units |
| Ground albedo | 0 to 1 | dimensionless |
| Relative humidity | 0 to 100 | percent |
| Near-surface temperature | 180 to 330 | K |
The generated data-pack manifest is the effective numerical domain. A point inside this outer envelope but outside the manifest still fails closed. No dimension may be extrapolated.
The horizon/window decision uses apparent altitude. Directional radiative transfer uses true line-of-sight geometry. Both values and the refraction model identity survive in the receipt. Refraction is applied exactly once.
The scalar apparent-horizon altitude remains the compatibility path. Phase 4 also admits an explicit caller-supplied circular azimuth/apparent-altitude terrain profile. The profile is never inferred from coordinates, downloaded, or substituted for a missing input. It must carry a profile identifier, source identifier, and lowercase SHA-256 source receipt.
The exact pack begins at 0.25 degrees true target altitude. When its refracted
apparent equivalent is above the caller’s scalar horizon, that pack floor
narrows the event window and is reported as
target_data_pack_altitude_floor. It is not mislabeled as a measured or
visual horizon.
For the directional profile:
[0, 360);[-5, 90);The profile applies to both the target and the Sun. The exact-pack target
floor remains a separate boundary and the effective target boundary is the
maximum of the directional terrain altitude and the refracted pack floor.
The event solver does not use apparent altitude - H(azimuth) as its
certified signal because azimuth is undefined at the zenith. Instead it uses
the local unit direction’s vertical component z, horizontal magnitude r,
and terrain altitude H(theta):
g = z - r * tan(H(theta))
For admitted altitudes, g has exactly the sign and zeros of
apparent altitude - H; at the zenith r = 0, so the signal is independent
of undefined azimuth. Let S be the profile’s maximum absolute
circular-linear slope, T the maximum abs(tan(H)), and Q the maximum
sec(H)^2. The horizontal cone has Lipschitz factor
K = sqrt(T^2 + (Q*S)^2). With the conservative admitted local-direction
angular-rate ceiling of 1024 degrees/day, the runtime certificate is:
maximum absolute signal rate =
radians(1024) * (1 + K) signal units/day
Taking the maximum with the constant pack floor preserves the slope ceiling;
the floor altitude is also included when computing T and Q. Profile
identity, resolution, S, K, queried azimuths, effective boundaries, and
certificate identity survive in the receipts. If the certificate cannot
exclude an unresolved crossing, the event fails closed.
Background authority is resolved in this order:
Only one authority may supply the same physical component. A measured background that already contains airglow, zodiacal light, integrated starlight, or artificial light cannot be combined with modeled copies of those components.
A separately modeled airglow, zodiacal-light, integrated-starlight, or artificial-light input is caller-supplied evidence, not an engine default. It must carry positive photopic/scotopic directional luminance, model and source identity, a source SHA-256, spatial/temporal/directional applicability, validity-domain identity, and uncertainty authority. Combining any such component with a dark-sky anchor requires the anchor to declare that its component inventory is complete. Duplicate component kinds, overlap with the anchor inventory, or combination with a measured total fail closed.
The engine receipts each separately modeled component. It does not silently include that component’s unadmitted scientific uncertainty in the data-pack numerical-error envelope.
An SQM input must record:
An unqualified scalar SQM value cannot become a spectral twilight background.
A named atmosphere profile must resolve and receipt every value it supplies. Pressure may be derived from altitude only through a named atmospheric profile, with the profile, equation, source, and derived value reported. Caller-supplied values override profile values only when the policy explicitly allows the override and the receipt marks it.
Required units are:
Bortle is a coarse compatibility input. It is never inferred from location and is used only when the caller explicitly selects the coarse fallback.
The physical reference table is not bundled in the MIT engine wheel.
It is a separately versioned, immutable, checksummed visibility data pack with:
The engine wheel may include only a metadata-only compatibility manifest. The runtime accepts an explicit caller-supplied data-pack path. Normal calculation never downloads or updates the pack.
Pack version 1.0 remains loadable for its Phase 1 atmospheric tables but contains no admitted planetary target profiles. Pack version 1.1 adds the source-locked Mercury-through-Saturn profiles required by the Phase 2 public assessment. Pack version 1.2 preserves every version 1.1 numerical payload byte and adds the source-locked Sirius profile required by Phase 3. Physical event search admits only version 1.2 with root manifest:
cf93433a9f66a5ea92832271ce3c4b023fcc8693164803539a9f1be85b17468c
Single-epoch assessment remains compatible with the admitted 1.0, 1.1, and 1.2 contracts according to their capabilities. Callers may pin the exact root manifest SHA-256 in both runtime configuration and policy.
Missing, incompatible, or corrupt data is a typed non-evaluable outcome.
An evaluated Phase 2 result carries both the nominal visibility margin and a separate declared data-pack numerical-error envelope. The envelope includes:
For modeled twilight, only the modeled twilight term is perturbed; the caller-supplied dark-sky anchor is held at its supplied value and its measurement uncertainty remains explicitly unquantified. For a measured-total background, Moira does not invent a background-error bound. It propagates only the pack-owned direct-extinction error and names the measurement uncertainty as unquantified.
The receipt reports limiting-magnitude and visibility-margin envelope limits
and one
classification within the pack numerical envelope: visible, not_visible,
or indeterminate. The ordinary visible field remains the nominal model
result. P95 interpolation diagnostics are not promoted into maximum bounds.
The reported solver term is not a hard maximum. This envelope is not a
probability, population model, or scientific confidence interval. Planetary
photometry, spectral-source, CIE/threshold model, observer-population,
real-atmosphere, and input-measurement uncertainty remain separate named
limitations.
libRadtran remains an external GPL reference generator. No libRadtran source, binary, Python binding, or runtime invocation enters the engine or data pack. Only generated numerical products, complete generator configuration, and provenance receipts may cross the build boundary.
This is the project’s packaging disposition, not legal advice. Release notice and artifact review remain mandatory before public distribution.
Representative additive Python types:
PhysicalVisibilityPhasePhysicalVisibilityStatusPhysicalVisibilityPolicyPhysicalVisibilityAssessmentPhysicalVisibilityEventResultVisibilityDataPackReceiptVisibilityComponentReceiptAdditive functions:
physical_visibility_assessment(...)
-> PhysicalVisibilityAssessment
physical_visibility_event(...)
-> PhysicalVisibilityEventResult
The assessment request carries:
jd_ut, latitude, and east-positive longitude;PhysicalVisibilityPolicy; andThe first fixed-star contract is Sirius and consumes only the pack-owned source-identified identity, Johnson V, and CALSPEC/CIE response. Other targets still require a separate admission gate.
The event request additionally carries:
PhysicalVisibilityPhase;search_window_days; andPhysicalVisibilityPolicy carries:
The local data-pack filesystem path is runtime configuration, not scientific
policy. Python receives it through a dedicated VisibilityDataPackConfig or
engine configuration. A REST client never supplies an arbitrary server path.
The REST deployment binds its allowed pack path and clients may assert only
the expected public pack identity/checksum.
Admitted dedicated REST routes:
POST /v1/visibility/physical-assessment
POST /v1/visibility/physical-event
The legacy /v1/visibility/assessment and
/v1/heliacal/visibility-event routes remain exact compatibility surfaces.
Every physical result returns a status:
evaluated: the requested truth was computed;not_evaluable: required evidence or domain support was absent; ornot_found: the search was evaluable but no requested phase transition
occurred inside the candidate interval.An event result includes:
observation_day_key;event_jd_ut;VisibilityDataPackReceipt includes:
The following identifiers are reserved:
visibility_data_pack_missingvisibility_data_pack_incompatiblevisibility_data_pack_checksum_mismatchephemeris_dependency_missingtarget_not_admittedtarget_spectral_profile_missingtarget_spectral_profile_context_missingtarget_spectral_profile_out_of_domaintarget_photometry_missingtarget_below_local_horizonsolar_twilight_below_data_pack_domainsolar_altitude_out_of_domaintarget_altitude_out_of_domainobserver_altitude_out_of_domainatmosphere_input_incompleteatmosphere_input_out_of_domainbackground_input_incompletebackground_component_inventory_incompletebackground_components_conflictlocal_horizon_coverage_missingsolar_rise_missingsolar_set_missingtarget_rise_missingtarget_set_missingno_valid_observation_windowcriterion_out_of_domainadaptation_state_incompleteobserver_protocol_not_admittedphase_ownership_not_evaluablesolver_domain_disconnectedbody_phase_not_admittedvisibility_event_data_pack_not_admittedcrossing_completeness_not_certifiedThe stable not_found reason is:
no_phase_transition_in_search_windowPolar day, polar night, circumpolarity, and never-rising geometry are not collapsed into one label. The specific missing solar or target boundary is reported.
The first physical admission excludes:
An exclusion is not a backlog defect. Reopening it requires a named source, new validity domain, additive contract proposal, independent validation, and an admission receipt.
The physical doctrine is frozen for implementation planning:
Any implementation that contradicts this document must stop and amend Phase 0 before continuing.