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. Usingreturned-speedshere 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,
contactsis omitted withmissing-aspectsand 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 |