@essrt/physics 20.0.1 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +4 -4
  2. package/package.json +4 -8
  3. package/src/cardinal/one-source-structure.cjs +3 -2
  4. package/src/chain-cycles/index.cjs +12 -30
  5. package/src/climate/l1-orbital.cjs +3 -1
  6. package/src/constants/coefficients.d.ts +17 -93
  7. package/src/constants/coefficients.js +7 -22
  8. package/src/constants/generated.d.ts +18 -156
  9. package/src/constants/generated.js +33 -168
  10. package/src/deltat/deep-time.cjs +36 -92
  11. package/src/deltat/recession-history.cjs +2 -1
  12. package/src/earth/deep-orbital-history.cjs +4 -4
  13. package/src/earth/frame-of-date.cjs +25 -1
  14. package/src/earth/precession-composed.cjs +3 -3
  15. package/src/earth/year-lengths.cjs +16 -1
  16. package/src/eclipse/sun-planetary-completion.cjs +11 -12
  17. package/src/index.js +28 -48
  18. package/src/layer0/derive-params.js +10 -5
  19. package/src/layer0/index.js +27 -73
  20. package/src/layer1/index.js +6 -37
  21. package/src/model.js +200 -225
  22. package/src/moon/arguments.cjs +57 -83
  23. package/src/moon/deep-modes-artifact.cjs +2 -2
  24. package/src/moon/month-chain.cjs +25 -59
  25. package/src/moon/series.cjs +14 -10
  26. package/src/phase/deep-calendar.cjs +35 -80
  27. package/src/planets/chain-artifact.js +2 -2
  28. package/src/planets/display-names.cjs +30 -0
  29. package/src/planets/geometry.cjs +79 -29
  30. package/src/planets/model.cjs +12 -11
  31. package/src/planets/spin-channel.cjs +46 -2
  32. package/types/chain-cycles/index.d.cts +9 -4
  33. package/types/climate/l1-orbital.d.cts +3 -1
  34. package/types/constants/coefficients.d.ts +17 -93
  35. package/types/constants/generated.d.ts +18 -156
  36. package/types/deltat/deep-time.d.cts +3 -16
  37. package/types/earth/frame-of-date.d.cts +9 -0
  38. package/types/earth/year-lengths.d.cts +12 -0
  39. package/types/eclipse/sun-planetary-completion.d.cts +0 -9
  40. package/types/index.d.ts +4 -13
  41. package/types/layer0/derive-params.d.ts +7 -4
  42. package/types/layer0/index.d.ts +6 -13
  43. package/types/layer1/index.d.ts +0 -2
  44. package/types/model.d.ts +8 -6
  45. package/types/moon/arguments.d.cts +23 -22
  46. package/types/moon/deep-modes-artifact.d.cts +3 -3
  47. package/types/moon/month-chain.d.cts +7 -16
  48. package/types/moon/series.d.cts +2 -4
  49. package/types/phase/deep-calendar.d.cts +14 -50
  50. package/types/planets/display-names.d.cts +2 -0
  51. package/types/planets/geometry.d.cts +36 -13
  52. package/types/planets/model.d.cts +7 -6
  53. package/types/planets/spin-channel.d.cts +4 -0
  54. package/src/cardinal/index.cjs +0 -324
  55. package/src/moon/ecc-channel.cjs +0 -141
  56. package/src/phase/index.cjs +0 -186
  57. package/src/planets/ecc-channel.cjs +0 -59
  58. package/src/sun/longitude-correction.cjs +0 -68
  59. package/types/cardinal/index.d.cts +0 -80
  60. package/types/moon/ecc-channel.d.cts +0 -30
  61. package/types/phase/index.d.cts +0 -38
  62. package/types/planets/ecc-channel.d.cts +0 -20
  63. package/types/sun/longitude-correction.d.cts +0 -41
package/src/model.js CHANGED
@@ -21,11 +21,11 @@ import { deriveEpochParams } from './layer0/derive-params.js';
21
21
  import { buildPlanetChainsFromArtifactData, computePlanetElementsAtYear } from './planets/keplerian-chain.cjs';
22
22
  import { createSecularSeriesOverride } from './planets/secular-series.cjs';
23
23
  import { computeEquatorNodeOriginSFrameDeg, convertNodeSFrameToEquatorOriginDeg } from './planets/inv-plane-frame.cjs';
24
- import { createPhaseMachinery } from './phase/index.cjs';
25
- import { createYearLengths, ONE_FAMILY_WINDOW_YEARS } from './earth/year-lengths.cjs';
24
+ import { createYearLengths, computeMeanTropicalYearJ2000Days, ONE_FAMILY_WINDOW_YEARS } from './earth/year-lengths.cjs';
26
25
  import { createDeepOrbitalHistory } from './earth/deep-orbital-history.cjs';
27
26
  import { CHAIN_ARTIFACT } from './planets/chain-artifact.js';
28
27
  import { createPlanetSpinChannelFromArtifacts, computeObliquityJ2000Deg } from './planets/spin-channel.cjs';
28
+ import { PLANET_DISPLAY_NAMES } from './planets/display-names.cjs';
29
29
  import { createDeltaTCycles } from './deltat/cycles.cjs';
30
30
  import { createDeepTimeLod } from './deltat/deep-time.cjs';
31
31
  import { createMoonRecessionHistory, createSolarChannelBudget } from './deltat/recession-history.cjs';
@@ -47,9 +47,9 @@ import { driver2PeriodSecondsAtAge } from './planets/orbit-chain.cjs';
47
47
  * engine too — packaging them into FITTED_COEFFICIENTS is the remaining
48
48
  * §7a-step-1b move; until then this is their single packaged home.
49
49
  */
50
- // (Plan 07 R10: the RA day offset's two cosine amplitudes left here as
51
- // literals; they ride F.RA_DAY_OFFSET_MS now — [div, sin, cos] on the J2000
52
- // phase, ONE home for the three runtimes and the website.)
50
+ // (Plan 07 R10 moved the RA day offset's two cosine amplitudes from literals
51
+ // here into F.RA_DAY_OFFSET_MS; plan 09 step 5b deleted that family — it was
52
+ // the equation of time's window term of an instrument, not a motion.)
53
53
 
54
54
  /**
55
55
  * Moon-channel eccentricity sensitivities (perigee/node), the [g/g₀]^s
@@ -86,24 +86,36 @@ const MOON_ECC_SENSITIVITY_NODE = 1.018;
86
86
  */
87
87
  export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type {any} */ (null), options = {}) {
88
88
  // ── Derived constants (constants.js §9 order) ─────────────────────────────
89
- const H = C.foundational.holisticyearLength;
90
- const meanSolarYearDays = Math.round(C.foundational.inputmeanlengthsolaryearindays * (H / 8)) / (H / 8);
91
- const startmodelYear = C.foundational.startmodelYear;
92
- const startmodelJD = C.foundational.startmodelJD;
93
- const startModelYearWithCorrection = startmodelYear + C.foundational.correctionDays / meanSolarYearDays;
89
+ // (plan 09 step 7b: `foundational.holisticyearLength` — the unit H — is no
90
+ // longer read here; the unit function left the physics with it)
91
+ // Plan 09: the mean solar year in days (the calendar's year axis) is the
92
+ // J2000 mean tropical year DERIVED in one home from the IAU sidereal year
93
+ // and p_A per Julian year (year-lengths.cjs); the Meeus & Savoie 1992 input
94
+ // 365.2421897 left the registry, the device's rounding onto 1/(H/8) before it.
95
+ const meanSolarYearDays = computeMeanTropicalYearJ2000Days(C.yearLengthRef.siderealYear, C.earthOrbital.generalPrecessionJ2000_arcsecPerYr);
96
+ // Plan 11 step 5: the scene epoch's ONE home — astro-reference
97
+ // timeReference.sceneEpochJD / sceneEpochYear (moved verbatim from the
98
+ // deleted foundational block); correctionDays is deleted, so the SI-year
99
+ // axis below anchors on the epoch itself.
100
+ const startmodelYear = C.timeReference.sceneEpochYear;
101
+ const startmodelJD = C.timeReference.sceneEpochJD;
94
102
  // Plan 07 R10: the correction combs measure their phase from J2000. The
95
103
  // former origin — perihelionalignmentYear − temperatureGraphMostLikely ×
96
104
  // H/16 = −302,635, the "balanced year" — was rotated out of every fitted
97
105
  // (sin, cos) pair exactly (tools/fit/reorigin-combs-j2000.mjs; measured
98
106
  // bit-identical on every surface over ±300 kyr); it is no longer an input.
99
- const balancedYear = 2000;
107
+ // Plan 09 step 5b: the J2000 comb origin's last reader (the RA day offset
108
+ // comb) left with that device; no comb phase is evaluated here any more.
100
109
 
101
110
  const meanSiderealYearDays = C.yearLengthRef.siderealYear;
102
111
  const meanSiderealYearSeconds = meanSiderealYearDays * 86400;
103
- const meanSiderealYearDaysKinematic = (meanSolarYearDays * H) / (H - 13);
104
- const meanLengthOfDay = meanSiderealYearSeconds / meanSiderealYearDaysKinematic;
105
- const meanAnomalisticYearDays = (meanSolarYearDays * (H / 16)) / (H / 16 - 1);
112
+ // Plan 09: the J2000 day length ω₀ is the SI day (an input); the device's
113
+ // H/13 kinematic-sidereal-days construction (86,399.99968 s) is deleted.
114
+ const meanLengthOfDay = C.yearLengthRef.solarDay;
106
115
  const meanTropicalYearJ2000Seconds = meanSolarYearDays * meanLengthOfDay;
116
+ // Plan 09: THE J2000 precession anchor p₀ in days (Julian years) — the IAU
117
+ // 2006 general precession; the Moon's star-referenced periods read it.
118
+ const axialPrecessionJ2000Days = (1296000 / C.earthOrbital.generalPrecessionJ2000_arcsecPerYr) * 365.25;
107
119
 
108
120
  // Plan 07 R5/R6/R9: `earthtiltMean`, `earthInvPlaneInclinationAmplitude`
109
121
  // and `eccentricityAmplitude` were read here only to invert the K and ψ
@@ -111,7 +123,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
111
123
  // device. With both laws retired nothing in this module reads them — they
112
124
  // remain on C.earth as the K device's J2000 pose inputs (the scene's wheel
113
125
  // geometry at the chain anchor epoch) and for the registry's record keys.
114
- const solsticeObliquityMean = F.SOLSTICE_OBLIQUITY_MEAN_FITTED;
126
+ // (Plan 09: the 16-harmonic solstice-obliquity comb — SOLSTICE_OBLIQUITY_
127
+ // MEAN_FITTED / _HARMONICS, the frozen era record — is deleted; the model's
128
+ // ONE obliquity of date is the one-source hybrid, `oneSourceM.epsAt`.)
115
129
 
116
130
  const G_CONSTANT = C.physicalConstants.G_CONSTANT;
117
131
  const MASS_RATIO_EARTH_MOON = C.physicalConstants.MASS_RATIO_EARTH_MOON;
@@ -120,9 +134,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
120
134
  const moonDistanceKm = C.moonReference.moonDistance;
121
135
  const moonSiderealMonthInput = C.moonReference.moonSiderealMonthInput;
122
136
 
123
- // 8H-lattice moon sidereal month (constants.js §"Moon derived months")
124
- const totalDaysInH = H * meanSolarYearDays;
125
- const moonSiderealMonth = totalDaysInH / (Math.round((8 * totalDaysInH) / moonSiderealMonthInput) / 8);
137
+ // Plan 09: the Moon's sidereal month is the IAU input itself (the device's
138
+ // eighth-integer-per-8H count read it back 7.8 ms long; deleted).
139
+ const moonSiderealMonth = moonSiderealMonthInput;
126
140
 
127
141
  // Mass chain: Moon Kepler → GM_EM → Earth/Moon split → Sun (§9 order)
128
142
  const moonOrbitalShift = moonDistanceKm * (1 / (MASS_RATIO_EARTH_MOON + 1)) * (moonSiderealMonth / meanSiderealYearDays);
@@ -160,15 +174,14 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
160
174
  j2J2000: C.physicalConstants.earthJ2,
161
175
  });
162
176
 
163
- let latticeAlphaPin = false;
164
177
  // Exact-argument memo (mirrors src/script.js + tools/lib/deep-time.js —
165
- // the round-5 perf campaign): same tMa → bit-identical value; the pin
166
- // branch bypasses the memo; bounded by wholesale clear.
178
+ // the round-5 perf campaign): same tMa → bit-identical value; bounded by
179
+ // wholesale clear. (Plan 09 step 7a: the lattice-α pin branch left with
180
+ // the ∫dt/H_era phase table, the one table this model built under it.)
167
181
  /** @type {Map<number, number>} */
168
182
  const alphaMemo = new Map();
169
183
  /** @param {number} tMa @returns {number} */
170
184
  const earthMoiFactorAtAge = (tMa) => {
171
- if (latticeAlphaPin) return earthMoiFactorJ2000;
172
185
  let v = alphaMemo.get(tMa);
173
186
  if (v === undefined) {
174
187
  v = alphaGia.alphaAt(2000 - tMa * 1e6);
@@ -186,9 +199,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
186
199
  alpha1PerMa: C.deepTime.alpha1PerMa,
187
200
  alpha3PerMa3: C.deepTime.alpha3PerMa3,
188
201
  alpha4PerMa4: C.deepTime.alpha4PerMa4,
189
- holisticYearJ2000: H,
190
202
  meanSiderealYearSeconds,
191
- meanSiderealYearDaysKinematic,
203
+ lodJ2000Seconds: meanLengthOfDay, // plan 09: ω₀ = the SI day (was the H/13 kinematic day)
192
204
  sunMassKg: M_SUN,
193
205
  gmEarthAloneKm3S2: GM_EARTH_ALONE,
194
206
  gmMoonAloneKm3S2: GM_MOON_ALONE,
@@ -203,6 +215,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
203
215
  astronomicalUnitKm: currentAUDistance,
204
216
  earthOrbitalEccentricityJ2000: C.earthOrbital.earthEccentricityJ2000,
205
217
  moonEclipticInclinationJ2000Deg: C.moonReference.moonEclipticInclinationJ2000,
218
+ // p₀'s period — the tropical year of date's equinox term (plan 09 step 5d)
219
+ axialPrecessionJ2000Years: certifiedAxialPrecessionJ2000Years(),
206
220
  });
207
221
 
208
222
  // ── Driver 1½: regime-aware recession history + the solar channels ────────
@@ -331,11 +345,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
331
345
  alpha1PerMa: EPOCH_PARAMS.alpha1PerMa,
332
346
  alpha3PerMa3: EPOCH_PARAMS.alpha3PerMa3,
333
347
  alpha4PerMa4: EPOCH_PARAMS.alpha4PerMa4,
334
- holisticYearJ2000: H,
335
348
  lodNowH13Seconds: EPOCH_PARAMS.lodNowH13Seconds,
336
349
  meanSiderealYearJ2000Seconds: meanSiderealYearSeconds,
337
350
  solarMassLossFracPerYear: EPOCH_PARAMS.solarMassLossFracPerYear,
338
- siderealYearDaysKinematicJ2000: meanSiderealYearDaysKinematic,
339
351
  precessionSolarShareJ2000: EPOCH_PARAMS.precessionSolarShareJ2000,
340
352
  },
341
353
  moonDistanceMetresAtAge,
@@ -346,43 +358,18 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
346
358
  // Plan 06 T2 item: the solar day's ecliptic missing-motion term rides the
347
359
  // nodal period 1,296,000/|s₃| (orbital), not the spin unit's H/5.
348
360
  nodalPeriodYearsFn: () => 1296000 / s3ArcsecPerYr,
349
- siderealYearDaysFourierAt: (year) => evalSiderealYearFourierIAU(year),
350
361
  cycleLodSumAt: dtCycleLodCorrectionSum,
351
362
  swingLodAt: (year) => dtCycles.swingLodSecondsAt(year),
352
363
  swingLodRateAt: (year) => dtCycles.swingLodRateAt(year),
353
364
  lEmAtAgeKgm2S: solarBudget.lEmAtAgeKgm2S,
354
365
  });
355
366
 
356
- // ── Integrated ∫1/H(t)dt phase (R2 α-pin honoured while building) ─────────
357
- // The phase counter every FROZEN comb reads rides the frozen era clock's
358
- // own convention H_era = H₀·LOD/LOD₀ (plan 06 D8: two named counters — the
359
- // coefficients were fitted against this counter and ship with it), NOT the
360
- // unit H(t) = 13·T_p,composed that the physics publishes.
361
- const phaseM = createPhaseMachinery({
362
- holisticHAtAgeMa: (tMa) => deepLod.eraClockHAtAge(tMa),
363
- tableAnchorYear: startmodelYear,
364
- driftRefYear: startModelYearWithCorrection,
365
- hJ2000: H,
366
- yearMin: -500e6,
367
- yearMax: 500e6,
368
- stepYears: 10000,
369
- });
370
- let phaseTableBuilt = false;
371
- const phase = () => {
372
- if (!phaseTableBuilt) {
373
- latticeAlphaPin = true;
374
- try { phaseM.ensureTable(); } finally { latticeAlphaPin = false; }
375
- phaseTableBuilt = true;
376
- }
377
- return phaseM;
378
- };
379
- /** @param {number} yearA @param {number} yearB @param {number} divisorN @returns {number} */
380
- const cyclesBetween = (yearA, yearB, divisorN) => {
381
- const cyc = phase().cyclesBetween(yearA, yearB, divisorN);
382
- return cyc === null ? (divisorN * (yearB - yearA)) / H : cyc;
383
- };
384
- /** @param {number} anchorYear @param {number} year @param {number} divisorN @returns {number} */
385
- const phaseRadians = (anchorYear, year, divisorN) => 2 * Math.PI * cyclesBetween(anchorYear, year, divisorN);
367
+ // (Plan 09 step 7a: the integrated ∫dt/H_era phase table and its counter
368
+ // `cyclesBetween` — the frozen era clock's own convention H_era =
369
+ // H₀·LOD/LOD₀, plan 06 D8's second named counter, which every FROZEN comb
370
+ // read — are DELETED with the last comb; `epoch.cyclesBetween` left the
371
+ // surface with them, no reader remained. Step 5b: `phaseRadians` left with
372
+ // the RA day offset comb.)
386
373
 
387
374
  /** @param {number} year @returns {number} */
388
375
  const yearToTMa = (year) => (2000 - year) / 1e6;
@@ -404,15 +391,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
404
391
  const SUN_OFF = F.EARTH_OSCULATING_MEAN_OFFSET;
405
392
  /** The eclipse Sun's ϖ of date (mean elements). @param {number} year @returns {number} */
406
393
  const sunPerihelionDegAt = laws.perihelionLongitudeDegAt ?? ((year) => oneSourceM.periOfDateDegAt(year) + SUN_OFF.dPomArcsec / 3600);
394
+ // Plan 09: the obliquity of date is the one-source hybrid everywhere — the
395
+ // eclipse tier (besselian deps) read the retired 16-harmonic comb here.
407
396
  /** @param {number} year @returns {number} */
408
- const obliquityDeg = (year) => {
409
- let obliq = solsticeObliquityMean;
410
- for (const [div, sinC, cosC] of F.SOLSTICE_OBLIQUITY_HARMONICS) {
411
- const ph = phaseRadians(balancedYear, year, div);
412
- obliq += sinC * Math.sin(ph) + cosC * Math.cos(ph);
413
- }
414
- return obliq;
415
- };
397
+ const obliquityDeg = (year) => oneSourceM.epsAt(year);
416
398
  // ECCENTRICITY UNIFICATION — ONE law for the whole model (plan
417
399
  // IP-eccentricity-unification, decision D1 = form (e)). Earth's orbital
418
400
  // eccentricity is the magnitude of a vector sum, e = |v₁ + v₂|: v₁ of
@@ -430,9 +412,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
430
412
  // lattice periods; H/16 is the OF-DATE perihelion period (13 + 3 = 16 —
431
413
  // the H/3 rotation seen from the H/13 equinox) and belongs to ϖ_of-date;
432
414
  // measured, the H/16 law's present ė (−0.84e-5/cy) is 5× below the
433
- // observed −4.20e-5 while this law reads −4.31e-5 (Phase-0 record:
434
- // tools/explore/fq7s-h3-law-candidate.mjs; JPL Sun 1.49″, registry 0.80″,
435
- // syzygy 3.72″). Consumers (engine-switch decision (ii) restatement,
415
+ // observed −4.20e-5 while this law reads −4.31e-5 (Phase-0 record: the
416
+ // H/3-law candidate instrument, deleted at plan 09 step 7e — docs/109 §22;
417
+ // JPL Sun 1.49″, registry 0.80″, syzygy 3.72″). Consumers (engine-switch decision (ii) restatement,
436
418
  // plan 02 §8): the eclipse Sun (equation of centre), the besselian Sun
437
419
  // distance and the cardinal-point braid — the CLOCK-side machinery. The
438
420
  // LUNAR chain (E-factor, perigee/node modulation/T², argument Δe² and
@@ -472,75 +454,51 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
472
454
  // one-source construction defined with the planets' chain evaluators below.)
473
455
 
474
456
  // ── Year/day lengths ──────────────────────────────────────────────────────
475
- /** @param {number} year @param {number} base @param {Array<[number, number, number]>} harmonics @returns {number} */
476
- const evalYearFourier = (year, base, harmonics) => {
477
- let result = base;
478
- const c1 = phase().cyclesBetween(balancedYear, year, 1);
479
- if (c1 === null) return result;
480
- for (const [div, sinC, cosC] of harmonics) {
481
- const ph = div * c1 * 2 * Math.PI;
482
- result += sinC * Math.sin(ph) + cosC * Math.cos(ph);
483
- }
484
- return result;
485
- };
486
- /** @param {number} year @returns {number} */
487
- const evalSiderealYearFourierIAU = (year) => evalYearFourier(year, meanSiderealYearDays, F.SIDEREAL_YEAR_HARMONICS);
488
- /** @param {number} year @returns {number} */
489
- const siderealYearDaysBase = (year) => {
490
- const tMa = yearToTMa(year);
491
- const lod = deepLod.lodSecondsAtAge(tMa);
492
- if (lod === null) return meanSiderealYearDays;
493
- return deepLod.siderealYearSecondsAtAge(tMa) / lod;
494
- };
495
- // The comb family's bases are the FROZEN era clock's forms (H_era; plan 06
496
- // D8) — the combs were fitted on them. The physical year lengths ride the
497
- // unit through yearLengths (family B) and the tidal-chain mean.
498
- /** @param {number} year @returns {number} */
499
- const tropicalYearDaysBase = (year) => {
500
- const days = deepLod.eraClockYearInDaysAtAge(yearToTMa(year));
501
- return days === null ? meanSolarYearDays : days;
502
- };
503
- /** THE J2000 precession anchor (plan 06 S5 — one J2000 precession
504
- * reading): the certified of-date year laws at 2000, sid/(sid − trop) =
505
- * 25,771.4 yr = 50.2883 ″/yr (IAU 50.2879 to 8×10⁻⁶) — identical to the
506
- * engine/browser Direct twins. ONE home: the composed clock's p₀, the
507
- * hybrid's self-anchor and the lunisolar surface all read it. The former
508
- * H/13 reading (25,793.6) is the fit anchor's, not a period. Memoized;
509
- * hoisted so the deep-time factory's lazy dep can name it.
510
- * @returns {number} */
457
+ // Plan 09: the three year-length combs (TROPICAL/SIDEREAL/ANOMALISTIC_
458
+ // YEAR_HARMONICS on the frozen era clock's bases) and their evaluators
459
+ // are deleted. The year lengths of date in DAYS are the published SI year
460
+ // lengths over the kinematic day of date; the kinematic day of date is the
461
+ // Layer-0 tidal mean LOD, exactly ω₀ (the SI day) at J2000 — the former
462
+ // "sidereal seconds over comb days" carried the λ̇ ripple of the sidereal
463
+ // year as a day-length ripple (≤ 0.1 ms in the era, 0.66 ms at ±26 kyr).
464
+ /** THE J2000 precession anchor p₀ (plan 09): the IAU 2006 general
465
+ * precession in longitude at J2000 (astro-reference.json
466
+ * earthOrbital.generalPrecessionJ2000_arcsecPerYr = 50.28796195 ″/yr),
467
+ * stated as the period 1,296,000/p_A = 25,771.575 Julian years. ONE home:
468
+ * the composed clock's p₀, the hybrid's self-anchor and the lunisolar
469
+ * surface all read it. Before plan 09 it was the retired year combs' beat
470
+ * at 2000 (plan 06 S5: 25,771.395 yr, 8 ppm faster); the H/13 reading
471
+ * (25,793.6) was the fit anchor's, not a period. The identifier keeps its
472
+ * name. @returns {number} */
511
473
  function certifiedAxialPrecessionJ2000Years() {
512
- if (certifiedAxial0Memo === undefined) {
513
- const sidLaw2000 = evalYearFourier(2000, siderealYearDaysBase(2000), F.SIDEREAL_YEAR_HARMONICS);
514
- const solLaw2000 = evalYearFourier(2000, tropicalYearDaysBase(2000), F.TROPICAL_YEAR_HARMONICS);
515
- certifiedAxial0Memo = sidLaw2000 / (sidLaw2000 - solLaw2000);
516
- }
517
- return certifiedAxial0Memo;
474
+ return 1296000 / C.earthOrbital.generalPrecessionJ2000_arcsecPerYr;
518
475
  }
519
- /** @type {number|undefined} */
520
- let certifiedAxial0Memo;
521
- /** @param {number} year @returns {number} */
522
- const anomalisticYearDaysBase = (year) => {
523
- const tMa = yearToTMa(year);
524
- const Ht = deepLod.eraClockHAtAge(tMa);
525
- const tropD = deepLod.eraClockYearInDaysAtAge(tMa);
526
- if (Ht === null || tropD === null) return meanAnomalisticYearDays;
527
- return (tropD * (Ht / 16)) / (Ht / 16 - 1);
528
- };
529
- /** @param {number} year @returns {number} */
530
- const siderealYearDays = (year) => evalYearFourier(year, siderealYearDaysBase(year), F.SIDEREAL_YEAR_HARMONICS);
531
- /** @param {number} year @returns {number} */
532
- const anomalisticYearDays = (year) => evalYearFourier(year, anomalisticYearDaysBase(year), F.ANOMALISTIC_YEAR_HARMONICS);
533
- /** Kinematic LOD (Layer 0). @param {number} year @returns {number} */
534
- const dayLengthSeconds = (year) => deepLod.siderealYearSecondsAtAge(yearToTMa(year)) / siderealYearDays(year);
535
- /** @param {number} year @returns {number} */
536
- const raDayOffsetMs = (year) => {
537
- let ms = F.RA_DAY_OFFSET_MS.mean;
538
- for (const [div, sinC, cosC] of F.RA_DAY_OFFSET_MS.terms) {
539
- const ph = phaseRadians(balancedYear, year, div);
540
- ms += sinC * Math.sin(ph) + cosC * Math.cos(ph);
541
- }
542
- return ms;
476
+ /** Kinematic day of date (Layer 0 tidal mean LOD; ω₀ at J2000). Past the
477
+ * tidal-lock asymptote the chain is null — the J2000 day stands in, as
478
+ * the retired comb base did. @param {number} year @returns {number} */
479
+ const dayLengthSeconds = (year) => deepLod.lodSecondsAtAge(yearToTMa(year)) ?? meanLengthOfDay;
480
+ /** Sidereal year of date in days of the epoch — the published SI sidereal
481
+ * year (the λ̇ channel inside the one-family window, the mass-loss law
482
+ * beyond) over the kinematic day of date. @param {number} year @returns {number} */
483
+ const siderealYearDays = (year) => publishedSiderealYearSecondsAtYear(year) / dayLengthSeconds(year);
484
+ /** Anomalistic year of date in days of the epoch — the published SI
485
+ * anomalistic year (one-family inside its window; NaN beyond, where the
486
+ * movement's apsidal line is not resolved). @param {number} year @returns {number} */
487
+ const anomalisticYearDays = (year) => {
488
+ const s = Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS ? yearLengthsM.anomalisticYearSecondsAtYear(year) : NaN;
489
+ return s / dayLengthSeconds(year);
543
490
  };
491
+ // (Plan 09 step 5b: raDayOffsetMs — the fitted RA_DAY_OFFSET_MS comb, a −14.194 ms
492
+ // mean + lines on H/16 and H/8 — and measuredSolarDaySeconds = dayLength + that
493
+ // offset are deleted. The quantity was the equation of time's window term of the
494
+ // Framework-Verification instrument: 365 solar noons counted from the June
495
+ // solstice end 0.2422 d short of the tropical year, so the mean noon-to-noon
496
+ // interval differs from the mean solar day by −(0.2422/365)·dEoT/dt at the
497
+ // solstice — the e·cos(90° − ϖ) term on the perihelion-of-date cycle (the comb's
498
+ // "H/16" line, 5.6 ms vs 5.2 first-order) and the tan²(ε/2) term on the obliquity
499
+ // cycle (the "H/8" line, 1.7 vs 1.4). A property of the window, not of the sky:
500
+ // the physical day of date is dayLengthSeconds; the equation of time is the
501
+ // certified Sun's. docs/11 § "The retired RA day offset".)
544
502
 
545
503
  // ── Cardinal points (the fitted model retired, R1)──────────────────────────────────────────────────
546
504
  // Plan 06 R1: the fitted cardinal-point model (CARDINAL_POINT_* — the §10
@@ -576,21 +534,21 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
576
534
  // per-tier sampler.
577
535
  const oneSourceM = (() => {
578
536
  const AEarth = /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements.earth;
579
- // The ψ̇ anchor: the CERTIFIED of-date laws at 2000 — identical to the
580
- // engine/browser (computeSiderealYearDaysDirect / computeSolarYearDays
581
- // Direct twins → 25,771.4). The FOUNDATIONAL mean pair used before
582
- // (meanSiderealYearDays/meanSolarYearDays → 25,796) anchored this
583
- // movement's realized precession 25 yr off the certified value — the
584
- // hidden split behind the API's 365.242204 tropical mean (found in the
585
- // owner's one-implementation drive).
537
+ // The ψ̇ anchor: the IAU 2006 general precession p_A (plan 09; 25,771.575
538
+ // yr — the ONE home certifiedAxialPrecessionJ2000Years, twins in
539
+ // tools-lib and the browser). History: plan 06 S5 anchored it on the
540
+ // certified year combs' beat at 2000 (25,771.4); before that the device's
541
+ // mean pair (meanSiderealYearDays/meanSolarYearDays → 25,796) anchored
542
+ // this movement's realized precession 25 yr off — the hidden split behind
543
+ // the API's 365.242204 tropical mean (found in the owner's
544
+ // one-implementation drive).
586
545
  const axial0 = certifiedAxialPrecessionJ2000Years(); // S5: the ONE home
587
546
  // D6 → Phase 3 (plan 06): the deep-time ψ̇(t) the hybrid precesses on is
588
- // the COMPOSED lunisolar rate, and since Phase 3 the unit H(t) IS
589
- // 13 × that period (deltat/deep-time.cjs hAtAge), so the injection is
590
- // the plain period₀·H(t)/H₀ again — on the unit, not on the frozen
591
- // era clock's H_era (which misses the lunar 1/a³ growth: 70.9 vs
592
- // Lantink 2022's 108.6 ± 8.5 ″/yr at 2.46 Ga; the unit reads 104.5).
593
- const H0 = /** @type {number} */ (deepLod.hAtAge(0));
547
+ // the COMPOSED lunisolar rate — its period of date T_p(t) read directly
548
+ // (plan 09 step 5c; it was period₀·H(t)/H₀ on the unit, the same number
549
+ // to 2e-16 since the unit scales with T_p). Not the frozen era clock's
550
+ // H_era (which misses the lunar 1/a³ growth: 70.9 vs Lantink 2022's
551
+ // 108.6 ± 8.5 ″/yr at 2.46 Ga; the composed rate reads 104.5).
594
552
  const seriesArt = /** @type {any} */ (secularSeriesArtifact);
595
553
  const sb = seriesArt && seriesArt.bodies && seriesArt.bodies.earth;
596
554
  const hist = createDeepOrbitalHistory({
@@ -612,10 +570,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
612
570
  // rendering-device residual imported as the initial condition of the
613
571
  // physics (found when the package began publishing the hybrid).
614
572
  obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
615
- axialPrecessionYearsAtYearFn: (yr) => {
616
- const h = deepLod.hAtAge((startmodelYear - yr) / 1e6);
617
- return axial0 * (h === null ? 1 : h / H0);
618
- },
573
+ axialPrecessionYearsAtYearFn: (yr) => deepLod.lunisolarPrecessionPeriodYearsAtAge((startmodelYear - yr) / 1e6) ?? axial0,
619
574
  // THE PRECESSION OF DATE (plan 06 §9 item 10): on top of the secular
620
575
  // law above, the solar torque at the eccentricity of date and the
621
576
  // oblateness of date from the GIA channel — both exactly 1 at J2000,
@@ -686,8 +641,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
686
641
  * ≡ the one-family year. Plan 06 R1: formerly the fitted cardinal model's
687
642
  * 4-mean. @param {number} year @returns {number} */
688
643
  const tropicalYearDays = (year) => oneFamilyTropicalYearSeconds(year) / dayLengthSeconds(year);
689
- /** @param {number} year @returns {number} */
690
- const tropicalYearDirectDays = (year) => evalYearFourier(year, tropicalYearDaysBase(year), F.TROPICAL_YEAR_HARMONICS);
691
644
 
692
645
  /** Layer-4 solar day of date in SI seconds — LOD_real, the shipped
693
646
  * observable (the tweakpane Solar Day row, the registry's lodRealPhysical,
@@ -727,8 +680,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
727
680
  const raProjection = Math.cos((oneSourceM.epsAt(year) * Math.PI) / 180);
728
681
  return (sidDay / Tp / (syD + 1)) * raProjection + sidDay;
729
682
  };
730
- /** @param {number} year @returns {number} */
731
- const measuredSolarDaySeconds = (year) => dayLengthSeconds(year) + raDayOffsetMs(year) / 1000;
732
683
 
733
684
  // ── Moon at epoch ─────────────────────────────────────────────────────────
734
685
  /** Solar-Δa-corrected Kepler month. @param {number} year @returns {number} */
@@ -793,7 +744,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
793
744
  /** @type {Record<string, Record<string, any>>} */
794
745
  const PUBLISHED_PLANET_RECORDS = {}; // what model.planets.record() serves (R8)
795
746
  for (const k of PLANET_KEYS) {
796
- const mp = C.planets[k];
797
747
  const ar = C.planetOrbitalElements[k];
798
748
  // Plan 06 Phase 7 commit 2: the K device's integer axial and obliquity
799
749
  // fractions are retired; the obliquity is the DERIVED J2000 obliquity of
@@ -834,10 +784,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
834
784
  const gArc = chainModes.g[k] && chainModes.g[k].arcsecPerYr;
835
785
  const sArc = chainModes.s[k] && chainModes.s[k].arcsecPerYr;
836
786
  PUBLISHED_PLANET_RECORDS[k] = Object.freeze({
837
- name: mp.name,
787
+ name: PLANET_DISPLAY_NAMES[k], // presentation — planets/display-names, the deleted JSON's last leaf (plan 11 step 6)
838
788
  solarYearInput: ar.solarYearInput,
839
789
  longitudePerihelion: ar.longitudePerihelion,
840
- ascendingNodeInvPlane: mp.ascendingNodeInvPlane,
790
+ ascendingNodeInvPlane: ar.ascendingNodeInvPlaneClosedForm, // the closed-form verified J2000 node — astro-reference since plan 11 step 2
841
791
  invPlaneInclinationJ2000: ar.invPlaneInclinationJ2000,
842
792
  orbitalEccentricityJ2000: ar.orbitalEccentricityJ2000,
843
793
  axialTiltJ2000: ar.axialTiltJ2000,
@@ -979,9 +929,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
979
929
  // put the eclipse umbra twin 8 km off (§12h; ≤1 m once unified).
980
930
  const siTropicalYearDays = meanTropicalYearJ2000Seconds / 86400;
981
931
  /** Model year (SI axis) at a JD(TT). @param {number} jd @returns {number} */
982
- const yearFromJD = (jd) => startModelYearWithCorrection + (jd - startmodelJD) / siTropicalYearDays;
932
+ const yearFromJD = (jd) => startmodelYear + (jd - startmodelJD) / siTropicalYearDays;
983
933
  /** JD(TT) at a model year (SI axis). @param {number} year @returns {number} */
984
- const jdFromYear = (year) => startmodelJD + (year - startModelYearWithCorrection) * siTropicalYearDays;
934
+ const jdFromYear = (year) => startmodelJD + (year - startmodelYear) * siTropicalYearDays;
985
935
 
986
936
  // ── Lunar theory: the shared-chain assembly (§7a slice-2b) ────────────────
987
937
  // Wiring mirrors the engine call sites exactly — tools/lib/deep-time.js
@@ -993,25 +943,26 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
993
943
  const j2000JD = 2451545.0;
994
944
  const julianCenturyDays = 36525;
995
945
 
996
- // J2000 Moon precession anchors (Option C+ — of-date observational anchors
997
- // in the legacy-'ICRF'-named inputs; the E values are star-referenced ∓13)
998
- const nApsidalIJ2000 = Math.round((8 * totalDaysInH) / C.moonReference.moonApsidalPrecessionDaysInputICRF) / 8;
999
- const nNodalIJ2000 = Math.round((8 * totalDaysInH) / C.moonReference.moonNodalPrecessionDaysInputICRF) / 8;
1000
- const nApsidalEJ2000 = nApsidalIJ2000 - 13;
1001
- const nNodalEJ2000 = nNodalIJ2000 + 13;
1002
- const moonApsidalJ2000Seconds = (totalDaysInH / nApsidalEJ2000) * meanLengthOfDay;
1003
- const moonNodalJ2000Seconds = (totalDaysInH / nNodalEJ2000) * meanLengthOfDay;
1004
- // the OF-DATE anchors (the H² counter's J2000 period — the Meeus/IERS
1005
- // observables 3231.49 d / 6798.38 d)
1006
- const moonApsidalOfDateJ2000Seconds = (totalDaysInH / nApsidalIJ2000) * meanLengthOfDay;
1007
- const moonNodalOfDateJ2000Seconds = (totalDaysInH / nNodalIJ2000) * meanLengthOfDay;
1008
- const moonSiderealMonthJ2000Seconds = moonSiderealMonth * meanLengthOfDay;
946
+ // J2000 Moon precession anchors (plan 09: the IAU inputs themselves). The
947
+ // legacy-'ICRF'-named inputs hold the EQUINOX-OF-DATE observables (Meeus/
948
+ // IERS 3231.50 d / 6798.38 d); the star-referenced (inertial) periods
949
+ // follow by removing the equinox's own precession, 1/T_E = 1/T_I ∓ 1/T_p
950
+ // (perigee advances, node regresses), T_p the J2000 precession anchor.
951
+ // The retired era device counted these as eighth-integers per 8H and read
952
+ // the periods back as totalDaysInH/N (the ∓13 was H/13 cycles per H).
953
+ const moonApsidalOfDateJ2000Seconds = C.moonReference.moonApsidalPrecessionDaysInputICRF * 86400;
954
+ const moonNodalOfDateJ2000Seconds = C.moonReference.moonNodalPrecessionDaysInputICRF * 86400;
955
+ const moonApsidalJ2000Seconds = 1 / (1 / moonApsidalOfDateJ2000Seconds - 1 / (axialPrecessionJ2000Days * 86400));
956
+ const moonNodalJ2000Seconds = 1 / (1 / moonNodalOfDateJ2000Seconds + 1 / (axialPrecessionJ2000Days * 86400));
957
+ const moonSiderealMonthJ2000Seconds = moonSiderealMonth * 86400;
958
+ // (Plan 09 step 5c: the per-H of-date cycle counts and the month chain's H²
959
+ // scaling of the of-date periods are deleted; the of-date perigee/node are
960
+ // the chain's count compositions.)
1009
961
 
1010
- // 8H-lattice derived months (constants.js §Moon derived months)
1011
- const nSid = Math.round((8 * totalDaysInH) / moonSiderealMonthInput) / 8;
1012
- const moonTropicalMonthDays = totalDaysInH / (nSid + 13);
1013
- const moonAnomalisticMonthDays = totalDaysInH / (nSid - nApsidalEJ2000);
1014
- const moonSynodicMonthDays = totalDaysInH / (nSid + 13 - H);
962
+ // Derived months on the IAU inputs (constants.js §Moon derived months)
963
+ const moonTropicalMonthDays = 1 / (1 / moonSiderealMonth + 1 / axialPrecessionJ2000Days);
964
+ const moonAnomalisticMonthDays = 1 / (1 / moonSiderealMonth - 86400 / moonApsidalJ2000Seconds);
965
+ const moonSynodicMonthDays = moonSiderealMonth * meanSiderealYearDays / (meanSiderealYearDays - moonSiderealMonth);
1015
966
 
1016
967
  // moonEcc — the model's ONE eccentricity law — is created above (with
1017
968
  // eccentricityAt); the Moon channel's E-factor e(t)/e(J2000) and its
@@ -1028,14 +979,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1028
979
  gmEarthMoonM3PerS2: EPOCH_PARAMS.gmEarthMoonM3S2,
1029
980
  massRatioEarthMoon: MASS_RATIO_EARTH_MOON,
1030
981
  moonSiderealMonthInputDays: moonSiderealMonthInput,
1031
- holisticYearJ2000: H,
1032
982
  meanSiderealYearJ2000Seconds: meanSiderealYearSeconds,
1033
- nApsidalOfDateJ2000: nApsidalIJ2000,
1034
- nNodalOfDateJ2000: nNodalIJ2000,
1035
983
  moonApsidalJ2000Seconds,
1036
984
  moonNodalJ2000Seconds,
1037
- moonApsidalOfDateJ2000Seconds,
1038
- moonNodalOfDateJ2000Seconds,
1039
985
  moonSiderealMonthJ2000Seconds,
1040
986
  sPerigee: MOON_ECC_SENSITIVITY_PERIGEE,
1041
987
  sNode: MOON_ECC_SENSITIVITY_NODE,
@@ -1043,7 +989,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1043
989
  fns: {
1044
990
  meanLodSecondsAtAge: /** @param {number} tMa */ (tMa) => deepLod.lodSecondsAtAge(tMa),
1045
991
  meanSiderealYearSecondsAtAge: /** @param {number} tMa */ (tMa) => deepLod.siderealYearSecondsAtAge(tMa),
1046
- meanHAtAge: /** @param {number} tMa */ (tMa) => deepLod.hAtAge(tMa),
992
+ // plan 09 step 5c: the tropical month's equinox turn is one per T_p(t)
993
+ precessionPeriodYearsAtAge: /** @param {number} tMa */ (tMa) => deepLod.lunisolarPrecessionPeriodYearsAtAge(tMa),
1047
994
  // Decision (ii) (owner-approved MODEL CHANGE, plan 02 §8): the whole
1048
995
  // lunar chain — this modulation, the integrated cycle counts it
1049
996
  // drives, the arguments' eccAt/channelIntegral and the E-factor —
@@ -1061,8 +1008,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1061
1008
  // Float64Array tables key correctly and persist.
1062
1009
  const chainCycles = createChainCycleIntegrator({
1063
1010
  ageAnchorYear: startmodelYear,
1064
- tropicalYearSecondsAtAge: /** @param {number} tMa */ (tMa) => deepLod.tropicalYearSecondsAtAge(tMa),
1065
- tropicalYearJ2000Seconds: meanTropicalYearJ2000Seconds,
1011
+ tropicalYearJ2000Seconds: meanTropicalYearJ2000Seconds, // the SI tick (plan 09 step 7c′)
1066
1012
  isDeepTime: () => true,
1067
1013
  });
1068
1014
  /** @param {number} tMa @returns {number|null} */
@@ -1071,6 +1017,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1071
1017
  const tropicalMonthPeriodFn = (tMa) => moonChain.tropicalMonthSecondsAtAge(tMa);
1072
1018
  /** @param {number} tMa @returns {number|null} */
1073
1019
  const anomalisticMonthPeriodFn = (tMa) => moonChain.anomalisticMonthSecondsAtAge(tMa);
1020
+ /** @param {number} tMa @returns {number|null} */
1021
+ const siderealMonthPeriodFn = (tMa) => moonChain.siderealMonthSecondsAtAge(tMa);
1074
1022
  const jupiterT0Seconds = C.planetOrbitalElements.jupiter.solarYearInput * 86400;
1075
1023
  /** @param {number} tMa @returns {number} */
1076
1024
  const jupiterPeriodFn = (tMa) => driver2PeriodSecondsAtAge(tMa, jupiterT0Seconds, EPOCH_PARAMS.solarMassLossFracPerYear);
@@ -1082,6 +1030,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1082
1030
  const mcAnomalistic = (a, b) => chainCycles.cyclesBetween(anomalisticMonthPeriodFn, a, b);
1083
1031
  /** @param {number} a @param {number} b @returns {number|null} */
1084
1032
  const mcJupiter = (a, b) => chainCycles.cyclesBetween(jupiterPeriodFn, a, b);
1033
+ /** The Moon's sidereal (star-referenced) orbits — the series' A3 argument (plan 09 step 5c). @param {number} a @param {number} b @returns {number|null} */
1034
+ const mcSidereal = (a, b) => chainCycles.cyclesBetween(siderealMonthPeriodFn, a, b);
1085
1035
  /** @param {number} a @param {number} b @returns {number|null} */
1086
1036
  const mcApsidalOfDate = (a, b) => {
1087
1037
  const t = mcTropical(a, b), n = mcAnomalistic(a, b);
@@ -1102,12 +1052,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1102
1052
  constants: {
1103
1053
  j2000JD,
1104
1054
  julianCenturyDays,
1105
- holisticYearJ2000: H,
1106
- balancedYearJ2000: balancedYear,
1107
- meanSolarYearDays,
1108
- meanAnomalisticYearDays,
1109
- tropicalYearHarmonics: F.TROPICAL_YEAR_HARMONICS,
1110
- anomalisticYearHarmonics: F.ANOMALISTIC_YEAR_HARMONICS,
1111
1055
  eccentricityJ2000: C.earthOrbital.earthEccentricityJ2000,
1112
1056
  eccentricityDotJ2000: C.earthOrbital.earthEccentricityDotJ2000,
1113
1057
  eccentricityDotDotJ2000: C.earthOrbital.earthEccentricityDotDotJ2000,
@@ -1123,22 +1067,31 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1123
1067
  // PUBLISHED ε (the one-source hybrid), not the K snapshot comb — one ε
1124
1068
  // everywhere. Twins: script.js (_sceneEpsTargetDeg), scene-graph.js.
1125
1069
  computeObliquityEarth: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
1070
+ // Plan 09: the arguments' Sun IS the one certified Sun — the mean
1071
+ // longitude that integrates the one-family tropical year (the eclipse
1072
+ // finder's Sun) and the series' mean perihelion of date (unwrapped),
1073
+ // both on the TT Julian-year axis. The former "model timeline" Sun
1074
+ // (360° per mean solar year plus the integral of the retired year
1075
+ // combs) sat 0.2° behind the IAU mean Sun at −2000 — the combs could
1076
+ // not carry the tropical year's secular drift. Twins: scene-graph.js,
1077
+ // script.js.
1078
+ sunMeanLonDegAtJdTT: /** @param {number} jdTT @returns {number} */ (jdTT) => sunMeanLongitudeDegAt(2000 + (jdTT - j2000JD) / 365.25),
1079
+ sunPerihelionLonDegAtJdTT: /** @param {number} jdTT @returns {number} */ (jdTT) => sunPerihelionDegAt(2000 + (jdTT - j2000JD) / 365.25),
1126
1080
  jdToSIyear: yearFromJD,
1127
1081
  tropicalOrbitsBetween: mcTropical,
1128
1082
  apsidalOfDateCyclesBetween: mcApsidalOfDate,
1129
1083
  nodalOfDateCyclesBetween: mcNodalOfDate,
1130
- cyclesBetween,
1131
1084
  isDeepTime: () => true,
1132
1085
  isFrameworkNative: () => true,
1133
1086
  // (d′) of-date rate completion: the DYNAMICAL axial precession (the
1134
1087
  // tweakpane day-form identity — real at J2000, epoch-valid) and the
1135
1088
  // KINEMATIC pair's beat the chains embed. MATCHED TRIPLE with the
1136
1089
  // tools-lib and browser wirings.
1137
- pDynDegPerYearAt: /** @param {number} year */ (year) => {
1138
- const sid = siderealYearDays(year);
1139
- const sol = tropicalYearDirectDays(year);
1140
- return 360 * (sid - sol) / sid;
1141
- },
1090
+ // Plan 09: the dynamical rate of date is the published precession
1091
+ // period's (the one-family beat inside its window, the composed clock
1092
+ // beyond) — the retired year combs' day-form beat read 60 % of the
1093
+ // secular drift. Degrees per Julian year.
1094
+ pDynDegPerYearAt: /** @param {number} year */ (year) => 360 / publishedAxialPrecessionYearsAtYear(year),
1142
1095
  pKinDegPerYearAt: /** @param {number} year */ (year) => {
1143
1096
  // MODEL-START-anchored age, matching the tools-lib/browser wirings
1144
1097
  // bit-exactly — NOT yearToTMa's 2000.0 convention (the recorded
@@ -1172,10 +1125,12 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1172
1125
  // Bounded Meeus E-factor from the ONE deep e (decision (ii); the
1173
1126
  // pure-Meeus polynomial A/B branch stays engine-local)
1174
1127
  /** @param {number} dDays @returns {number} */
1175
- const fwEFactor = (dDays) => deepEcc.eFactorAt(dDays / C.foundational.inputmeanlengthsolaryearindays);
1128
+ // (plan 09 step 7c: on the channel's Julian-year axis — the N-body series'
1129
+ // time; the device's rounded tropical-year input read the axis 2e-5 short)
1130
+ const fwEFactor = (dDays) => deepEcc.eFactorAt(dDays / 365.25);
1176
1131
 
1177
- // D2 derived additional-argument rates (deg/cy, J2000 8H-lattice months;
1178
- // record: tools/explore/derive-a1a2a3.js)
1132
+ // D2 derived additional-argument rates (deg/cy, from the J2000 month inputs;
1133
+ // the derivation record derive-a1a2a3.js was deleted at plan 09 step 7e — docs/109 §22)
1179
1134
  const fwA2RateDegPerCy = 2 * ((360 * 36525) / moonTropicalMonthDays)
1180
1135
  - (360 * 36525) / moonAnomalisticMonthDays
1181
1136
  - 2 * ((360 * 36525) / C.planetOrbitalElements.jupiter.solarYearInput);
@@ -1191,7 +1146,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1191
1146
  moonDistanceJ2000Km: C.moonReference.moonDistance,
1192
1147
  j2000JD,
1193
1148
  julianCenturyDays,
1194
- moonMeeusLpCorrectionDeg: C.moon.moonMeeusLpCorrection,
1195
1149
  fwA2RateDegPerCy,
1196
1150
  fwA3RateDegPerCy,
1197
1151
  },
@@ -1203,14 +1157,15 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1203
1157
  // evaluator). MATCHED TRIPLE with the tools-lib and browser getters:
1204
1158
  // identical age arithmetic — the SI-linear year (yearFromJD, never
1205
1159
  // the calendar year: the recorded linear-vs-calendar mirror trap)
1206
- // against the MODEL-START anchor (startModelYearWithCorrection, the
1207
- // browser's J2000_CALENDAR_YEAR — NOT this model's 2000.0 yearToTMa
1208
- // convention: the 0.4977-yr difference is 4.95e-11 of distance and
1209
- // failed the bit-exact parity gate). Falls back to the J2000
1210
- // constant without a jd (legacy call shape) or past the domain.
1160
+ // against the MODEL-START anchor (startmodelYear, the browser's
1161
+ // J2000_CALENDAR_YEAR — NOT this model's 2000.0 yearToTMa convention:
1162
+ // the 0.5-yr difference is 4.95e-11 of distance and failed the
1163
+ // bit-exact parity gate; plan 11 step 5 dropped the −0.83-d correction
1164
+ // from the anchor, both twins alike). Falls back to the J2000 constant
1165
+ // without a jd (legacy call shape) or past the domain.
1211
1166
  getMoonDistanceKm: /** @param {number} [jdTT] */ (jdTT) => {
1212
1167
  if (jdTT === undefined) return moonDistanceKm;
1213
- const d = moonDistanceMetresAtAge((startModelYearWithCorrection - yearFromJD(jdTT)) / 1e6);
1168
+ const d = moonDistanceMetresAtAge((startmodelYear - yearFromJD(jdTT)) / 1e6);
1214
1169
  return d === null ? moonDistanceKm : d / 1000;
1215
1170
  },
1216
1171
  getEccentricityBase: () => C.moonReference.moonOrbitalEccentricityBase,
@@ -1218,7 +1173,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1218
1173
  jdToSIyear: yearFromJD,
1219
1174
  tropicalOrbitsBetween: mcTropical,
1220
1175
  apsidalOfDateCyclesBetween: mcApsidalOfDate,
1221
- cyclesBetween,
1176
+ siderealOrbitsBetween: mcSidereal,
1222
1177
  jupiterOrbitsBetween: mcJupiter,
1223
1178
  isDeepTime: () => true,
1224
1179
  isFrameworkNative: () => true,
@@ -1330,6 +1285,14 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1330
1285
  };
1331
1286
  return /** @param {number} year @returns {number} */ (year) => sunL0Deg + 360 * cyclesTo(year);
1332
1287
  })();
1288
+ // Plan 09: the lunar arguments' Sun perihelion of date is the eclipse
1289
+ // Sun's mean perihelion (`sunPerihelionDegAt`) read DIRECTLY, wrapped.
1290
+ // Every consumer wraps the argument it enters (M = Lsun − ϖ − 180) or
1291
+ // differences it over ±25 days (the rate anchors, wrapped to ±180°), so
1292
+ // no continuous angle is needed. MEASURED: a 100-yr unwrapping ladder
1293
+ // (two series reads per rung, 53,400 rungs at −5.34 Myr) cost the cold
1294
+ // deep jump 1,062 s — 343× the in-table jump against the perf gate's 8× —
1295
+ // for a value the consumers reduced mod 360 anyway.
1333
1296
 
1334
1297
  // Eclipse finders — wired like the engine probe (tools/verify/
1335
1298
  // eclipse-audit.js). The finder axis is JD(UT): the series wrapper applies
@@ -1453,6 +1416,13 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1453
1416
  if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.tropicalYearSecondsAtYear(year);
1454
1417
  return deepLod.tropicalYearSecondsAtAge(yearToTMa(year));
1455
1418
  };
1419
+ /** The published sidereal year of date, SI seconds — the one-family λ̇
1420
+ * channel inside the window, the mass-loss law beyond (plan 09: the
1421
+ * `lengths` surface's days ride it). @param {number} year @returns {number} */
1422
+ const publishedSiderealYearSecondsAtYear = (year) => {
1423
+ if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.siderealYearSecondsAtYear(year);
1424
+ return deepLod.siderealYearSecondsAtAge(yearToTMa(year));
1425
+ };
1456
1426
  // ── Cardinal instants (plan 06 R1) ─────────────────────────────────────
1457
1427
  /** The completed certified Sun (finder Sun − the derived planetary
1458
1428
  * completion) at a UT model-JD — the same longitude the scene renders
@@ -1546,9 +1516,20 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1546
1516
  jdFromYear,
1547
1517
  siTropicalYearDays,
1548
1518
  }),
1519
+ // Plan 09: the ONE certified Sun's mean elements of date — the lunar
1520
+ // arguments of every runtime build their Sun from these two (the Node
1521
+ // scene twin and the browser read them here; the "model timeline" Sun on
1522
+ // the retired year combs is gone).
1523
+ sun: Object.freeze({
1524
+ /** Mean longitude of date, degrees, unwrapped — L0 + 360·∫dt/T_trop (the one-family tropical year). @param {number} year @returns {number} */
1525
+ meanLongitudeDegAt: sunMeanLongitudeDegAt,
1526
+ /** The Sun's mean perihelion longitude of date (Earth's ϖ, mean elements), degrees, wrapped to [0, 360). @param {number} year @returns {number} */
1527
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
1528
+ }),
1549
1529
  epoch: Object.freeze({
1550
1530
  yearToTMa,
1551
- hAtYear: /** @param {number} year @returns {number|null} */ (year) => deepLod.hAtAge(yearToTMa(year)),
1531
+ // (plan 09 step 7b: `hAtYear` — the unit H(t) on a published surface, the
1532
+ // API's `epoch.h` — is deleted; the deep-time clock is axialPrecessionYearsAtYear)
1552
1533
  // The Layer-2 mean day of date — the tidal + GIA base the deep-time law
1553
1534
  // gives at any age (86,399.9997 s at J2000): the day the paleo anchors
1554
1535
  // and the ΔT integrand ride. NOT the observable solar day of date —
@@ -1564,18 +1545,17 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1564
1545
  // (identical at J2000 where the ratio ≡ 1 by construction)
1565
1546
  siderealYearSecondsAtYear: /** @param {number} year @returns {number} */ (year) => yearLengthsM.siderealYearSecondsAtYear(year),
1566
1547
  deltaTSecondsAtYear: deltaTSeconds,
1567
- cyclesBetween,
1568
1548
  // Plan 06 Phase 3 S2 — THE published of-date precession period is the
1569
1549
  // one-family route (B): the hybrid's own equinox regression against
1570
1550
  // the λ̇-corrected sidereal year (~25,771 yr at J2000 ≈ IAU), inside
1571
1551
  // the banked tiers (|Δyear| ≤ ONE_FAMILY_WINDOW_YEARS); beyond, the
1572
1552
  // composed lunisolar period on the same J2000 anchor (S5; the
1573
- // of-date wobble is unresolved there). The comb family's beat
1574
- // sid/(sid − tropicalYearDirectDays) — the frozen era clock's
1575
- // kinematic-day device — is no longer published here; it still
1576
- // anchors the lunar chain's (d′) rate completion (pDynDegPerYearAt,
1577
- // a MATCHED TRIPLE with the eclipse gates: M0(a) measured a 3–15 min
1578
- // lunar-timing shift if it moved).
1553
+ // of-date wobble is unresolved there). The comb family's beat (the
1554
+ // frozen era clock's kinematic-day device) is deleted at plan 09; the
1555
+ // lunar chain's (d′) rate completion (pDynDegPerYearAt) rides THIS
1556
+ // published period — a MATCHED TRIPLE with the eclipse gates (M0(a)
1557
+ // measured a 3–15 min lunar-timing shift when it moved; the records
1558
+ // were re-baselined on the plan 09 move).
1579
1559
  axialPrecessionYearsAtYear: publishedAxialPrecessionYearsAtYear,
1580
1560
  // The same publication rule for the mean tropical year of date (SI s):
1581
1561
  // one-family inside the window, the tidal-chain mean beyond.
@@ -1686,12 +1666,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1686
1666
  // Plan 06 Phase 3 S3b: THE published obliquity is the hybrid — one
1687
1667
  // torque law integrated on the dynamical orbit plane of the N-body
1688
1668
  // chain, zero fitted constants (≡ La2004 to ≤ 4″ over ±50 kyr). The
1689
- // fitted 16-harmonic K comb, a device fitted to the scene's own
1690
- // wheel geometry (0.005″ to it, 1.27° off La2004 at −20 kyr), keeps
1691
- // its name below as the frozen era clock's device: it anchors the
1692
- // hybrid at J2000, the kinematic-day stack and the lunar arguments.
1669
+ // fitted 16-harmonic K comb that sat beside it as `obliquityCombDeg`
1670
+ // (a device fitted to the scene's own wheel geometry, 1.27° off
1671
+ // La2004 at −20 kyr) is deleted with the frozen era device (plan 09).
1693
1672
  obliquityDeg: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
1694
- obliquityCombDeg: obliquityDeg,
1695
1673
  eccentricity: eccentricityAt,
1696
1674
  inclinationDeg,
1697
1675
  ascendingNodeDeg,
@@ -1703,15 +1681,12 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1703
1681
  // route) and the seamed `epoch.*AtYear` surfaces above.
1704
1682
  lengths: Object.freeze({
1705
1683
  tropicalYearDays,
1706
- tropicalYearDirectDays,
1707
1684
  siderealYearDays,
1708
1685
  anomalisticYearDays,
1709
1686
  dayLengthSeconds,
1710
1687
  lodRealSeconds,
1711
1688
  siderealDaySeconds,
1712
1689
  stellarDaySeconds,
1713
- measuredSolarDaySeconds,
1714
- raDayOffsetMs,
1715
1690
  }),
1716
1691
  // Plan 06 R1: the instants are the crossings of the ONE Sun (the
1717
1692
  // certified completed Sun the scene renders), UT model-JD; the RA at a