Astrology8 MIN READ52 / 116

Secondary Progressions secondary-day-for-year@1 — Specification (W-05)

Status: Implemented v1 convention · Task: W-05 · Updated: 2026-10-25 · Implementation: W-07 package.

A progression maps a target date to a calculation instant, then asks Composer for real astronomy at that instant. Nothing is extrapolated from the birth chart: the progressed chart is an observed chart for a derived moment. The technique’s whole convention lives in the mapping and in the angle policy.

1. Admitted variant

Field Value
Technique progressions
Version 1
Variant secondary-day-for-year
Rulebook entry { "technique": "progressions", "version": "1", "variant": "secondary-day-for-year", "hash": "<payload hash>" }

Rule payload (validated by validateProgressionRules):

{
  "method": "secondary",
  "timeUnit": "day-for-tropical-year",
  "yearLength": "tropical-2000",
  "angles": "naibod-mc",
  "direction": "forward"
}

Every field is required. calendar-day-for-year, julian-year, solar-arc-progressed-angles, tertiary, minor and converse are different variants (§11).

2. Inputs

Input Source Meaning
Reference snapshot Engine Birth/starting chart; supplies instant, bodies, angles and observer
targetInstant Request Absolute instant being progressed to (ISO with offset or epoch ms)
Profile Engine Zodiac, bodies, node model, position origin, aspect policy
Injected ComposerPort Engine (prerequisite P0) Composes the mapped chart
Injected GeometryPort Optional Progressed-to-natal contacts

The reference observer is reused unchanged for the mapped read. Progressions never choose a new location and never convert zodiac, house system or node model (mean or true node).

3. Time mapping

3.1 Constants (part of the version)

Name Value Meaning
YEAR_DAYS 365.242190 Mean tropical year (J2000, the standard reference epoch 2000-01-01 12:00 TT)
YEAR_MS 31 556 925 216 YEAR_DAYS × 86 400 000, exactly
NAIBOD 0.9856473590852142 360 / YEAR_DAYS degrees per ephemeris day

3.2 Mapping

t0      = reference snapshot context instant (epoch ms)
tt      = targetInstant (epoch ms)
elapsed = tt - t0

require elapsed >= 0                      // forward only

elapsedYears  = elapsed / YEAR_MS         // tropical years of life
ephemerisDays = elapsedYears              // 1 day of ephemeris time per tropical year
t_p           = t0 + round(ephemerisDays × 86 400 000)   // integer ms, ties away from zero

round is applied once, to whole milliseconds, and the rounded instant is the one composed and retained. The year length is the only unit conversion in the technique; there is no separate “age in years” field and no calendar arithmetic (leap days, timezones, anniversaries) anywhere in the mapping.

The identity of the mapping is (t0, tt, YEAR_MS, rounding); two callers asking for the same tt under the same reference get the same t_p.

3.3 Bodies

The mapped chart is composed with the profile’s context at t_p:

toChartContextInput(profile, {
  instant: t_p,
  positionObserver: reference.context.positionObserver,   // when topocentric
  houseObserver: undefined
}, { houses: false })

Every body position is observed astronomy at t_p, not a transformation of the natal longitude. Its origin is { kind: 'observed', snapshot: <mapped snapshot hash> }.

4. Progressed angles

Angle v1 policy
Midheaven mc_p = wrap360(mc_0 + arc)
Ascendant Not derived — null with missing-angles
Other angles Not derived
arc = NAIBOD × ((t_p - t0) / 86 400 000)

The arc uses the rounded t_p, so the value is replayable from the retained instant. It is a derived quantity: the progressed MC carries { kind: 'derived', method: 'progressions.naibod-mc', version: '1', sources: [<reference hash>, <mapped hash>] }.

The progressed ascendant is deliberately absent. Deriving it needs the progressed MC and the birth latitude through an ascendant formula that Composer does not expose; attaching the natal ascendant unchanged, or recomputing one here, would both be fabrication. A future naibod-angles@2 variant that advances both angles by the arc needs its own fixtures.

If the reference snapshot has no midheaven, the progressed MC is null with missing-angles; the bodies still progress.

5. Houses

secondary-day-for-year@1 produces no progressed houses. The mapped context requests positions only, the result carries houses: null, and a request for progressed cusps is LAYER_UNSUPPORTED. Natal cusps are never reused as progressed cusps.

6. Contacts (optional)

When geometry is connected, the progressed chart may be compared with the natal chart:

policy   = { ...profile.aspectPolicy, motion: 'fixed-target' }
contacts = geometry.evaluateCrossChartAspects(progressedGeometry, natalGeometry, policy)
  • The progressed chart is the source (it moves); the natal chart is the fixed target.
  • motion: 'fixed-target' is what makes the natal derivative zero. Using returned-speeds here would treat the natal chart as moving at its birth-time speed, which is the W-03/W-04 defect in a different technique.
  • Contacts are optional: with geometry absent, contacts is omitted with missing-aspects and the progressed chart is still a successful result.

7. Result

Field Meaning
method { id: 'secondary-day-for-year', version: '1', yearLength: 'tropical-2000', angles: 'naibod-mc' }
reference { instant: t0, snapshot: <hash> }
target { instant: tt }
mapped { instant: t_p, ephemerisDays, arc, snapshot: <mapped hash> }
bodies[] Observed positions at t_p, in profile order
angles { midheaven: { longitude, origin }, ascendant: null }
houses Always null
contacts? Progressed-to-natal contacts (§6)
missing missing-angles, missing-aspects

The result retains reference date, target date, mapped instant, arc, both snapshot hashes and the technique identity, so the call is replayable without recomputing the mapping.

8. Composer facts and ports

Need Provided by Absent behaviour
Reference positions, angles, observer Reference snapshot INSUFFICIENT_INPUT
Astronomy at t_p ComposerPort.compose (P0) missing-capability (positions)
Aspect evaluation GeometryPort missing-aspects only
ProbePort Not used — one instant, no search —

Declared requirements: snapshot: 'none', composer: true, upstream: ['positions'], optionalUpstream: ['geometry'], dependencies: [].

9. Failures

Case Behaviour
targetInstant before the reference instant INVALID_REQUEST; no converse fallback
Unparsable/absent targetInstant INVALID_REQUEST
Unknown method, timeUnit, yearLength, angles or direction INVALID_RULES
Mapped compose fails Upstream error preserved; layer fails
Reference lacks the midheaven missing-angles, bodies still returned
Progressed houses requested LAYER_UNSUPPORTED
Geometry absent missing-aspects only

10. Worked examples

t0 = 2000-01-01T00:00:00.000Z throughout. Values are exact at the stated precision.

E1 — exactly 30 tropical years. tt = t0 + 30 × YEAR_MS = 2029-12-31T06:22:36.480Z.

elapsed       = 30 × 31 556 925 216 ms
ephemerisDays = 30
t_p           = 2000-01-31T00:00:00.000Z
arc           = 0.9856473590852142 × 30 = 29.569420772556°  -> 29.5694207726°
mc_0 = 100°   -> mc_p = 129.5694207726°

E2 — the calendar 30th anniversary. tt = 2030-01-01T00:00:00.000Z is 10 958 days after t0, not 30 tropical years:

ephemerisDays = 10958 / 365.242190 = 30.0020104468216
t_p           = 2000-01-31T00:02:53.703Z      (offset 2 592 173 702.605 ms, rounded)
arc           = 29.571402364157°  -> 29.5714023642°

E1 and E2 differ by 2 min 53.703 s of mapped time and 0.0019816° of arc. A fixture that treats a calendar anniversary as exactly 30 years fails E2 by design.

E3 — one day of life.

elapsed       = 86 400 000 ms
ephemerisDays = 86 400 000 / 31 556 925 216 = 0.002737909331
offset        = 236 555.3662 ms  -> round 236 555
t_p           = 2000-01-01T00:03:56.555Z
arc           = 0.002698613101°

E4 — degenerate. tt = t0 maps to t_p = t0, arc = 0, and returns the natal chart. Allowed; the result is identical to the reference positions with derived MC equal to the natal MC.

E5 — backwards. tt = t0 - 1 ms → INVALID_REQUEST; no converse progression is substituted.

E6 — missing angles. Reference composed position-only → angles.midheaven = null, missing-angles, bodies still progressed.

E7 — topocentric. Reference (topocentric: measured from the observer’s place on Earth rather than Earth’s centre) positionOrigin: 'topocentric' with observer O → every probe and the mapped compose use O; a request cannot supply a different observer.

11. Unsupported variants

Variant Why unsupported
calendar-day-for-year Maps by calendar anniversary; different t_p for every birth date
julian-year (365.25) Different YEAR_MS; changes t_p and the arc
solar-arc-progressed-angles Progressed angles from the progressed Sun; needs its own specification and the true-arc dependency
naibod-angles (ASC advanced by the arc) A second angle convention; must be fixtured separately
tertiary, minor (tertiary and minor progression) Different time units (1 lunar month/day, 1 lunar month/year)
converse Backwards mapping; requires a negative direction policy
Progressed houses No reviewed construction; cusps are never inherited

12. Prerequisites before W-07

The implementation and coordinator integration now satisfy these runtime seams; convention sources remain the responsibility of each supplied production rulebook. See the package tests.

Id Prerequisite Owner
P0 The feature can reach a ComposerPort to compose the mapped chart W-11 owner (index)
P1 A reviewed source for the day-for-a-year mapping, the tropical year length and the Naibod rate Progression reviewer
P2 progressions.secondary-day-for-year@1 payload schema and validator registered W-07
P3 A fixture host that returns a deterministic synthetic position for any requested instant, so E1–E3 need no ephemeris W-07