Current roadmap. Active work now follows the six engine gates in HELLENISTIC_ENGINE_GATES_2026-07.md. Its Phase 2 is the doctrine-decision gate and is not the historical “Bounds Expansion and Halb” phase retained below.
Phase-1 correction (2026-07-25). The roadmap body remains historical. Whole-sign direction and overcoming are now implemented and tested. The Decennials public computation boundary ends at L2. The source-reconstructed Gundel/Harley Hermetic name-and-face catalog is a research-only closed product exclusion from package-root, facade, and registered REST surfaces. See the current backend standards rather than the old status tables below. Status language in the archived body is historical and must not create roadmap work.
Phase-2 doctrine correction (2026-07-25). The former “exact-two-of-three” Halb plan was rejected after a source pass.
is_in_halb()now implements the al-Qabisi/Bonatti sect-relative hemisphere rule; Hayz adds the planet’s own sign gender. Mars is feminine and Mercury is neutral. Missing Sun or Mercury phase truth is no longer fabricated. Annual profection schedules use completed civil anniversaries, and Decennials transport preserves its dual time-basis receipt. Valens IV.4 now source-locks the Spirit/Fortune same-sign start shift and exact 211-month Loosing-of-the-Bond behavior. The conflicted lots have source-specific names and explicit projector/arc policies. The Harley MS 3731 Hermetic names and planetary faces are reconstructed from Gundel’s identified edition, while their unsupported fixed-star assignments fail closed.
Note (2026-05-29 code verification): A documentation + code verification pass against this roadmap (cross-referenced with WESTERN_HELLENISTIC_GAP_TRACKER.md and FEATURE_AUDIT_2026.md) found that many items listed as “Partial” or “Absent” below are now implemented in the live codebase (e.g., first-class triplicity/bound/face in EssentialDignityKind, PLANETARY_JOYS + is_in_joy, PTOLEMAIC/CHALDEAN bounds tables + doctrine support, is_in_halb + SCORE_HALB, oriental_occidental(), is_besieged(), AspectDomain.WHOLE_SIGN + find_whole_sign_aspects()). The roadmap text below is retained for historical reference but is partially outdated.
This is an additive roadmap governing the closure of partial and absent Hellenistic astrology features in Moira.
Moira already implements the structural pillars of Hellenistic technique: sect, lots, whole sign houses, zodiacal releasing, profections, firdaria, antiscia, essential dignities (domicile/exaltation), fixed stars with parans and heliacal visibility, and degree-based Ptolemaic aspects. What remains are doctrinal refinements and small subsystems that complete the Hellenistic apparatus to the level where the tradition can be practised without silent gaps.
The work is grouped into four phases of increasing interpretive depth. Each phase is independently shippable and testable.
Companion documents:
| Feature | Module |
|---|---|
| Sect (diurnal/nocturnal, sect light, hayz) | moira/dignities.py |
| Lots (~430 named, day/night reversal) | moira/lots.py, moira/nine_parts.py |
| Whole Sign Houses | moira/houses.py |
| Essential dignities (domicile, exaltation, detriment, fall, peregrine) | moira/dignities.py |
| Triplicity rulers (Dorotheus, scored in longevity) | moira/longevity.py |
| Face/decan rulers (Chaldean, scored in longevity) | moira/longevity.py |
| Egyptian bounds (full table, profiling, network) | moira/egyptian_bounds.py |
| Profections (annual + monthly, classical rulers) | moira/profections.py |
| Zodiacal releasing (Fortune, Spirit, Eros, Necessity) | moira/timelords.py |
| Firdaria (diurnal, nocturnal, Bonatti) | moira/timelords.py |
| Antiscia + contra-antiscia | moira/antiscia.py |
| Aspects (Ptolemaic five, applying/separating, patterns) | moira/aspects.py |
| Fixed stars (Behenian, Royal, parans, heliacal) | multiple modules |
| Solar condition (cazimi, combust, under the beams) | moira/dignities.py |
| Mutual reception (domicile + exaltation) | moira/dignities.py |
Note: Several items below have been implemented since this roadmap was drafted. See code verification in WESTERN_HELLENISTIC_GAP_TRACKER.md.
| Feature | What exists | What is missing (per original) | Verification Status |
|---|---|---|---|
| Bounds/terms | Egyptian bounds only | Ptolemaic terms, Chaldaean terms | Corrected: source-transmitted PTOLEMAIC_BOUNDS plus explicit CHALDEAN_DAY_BOUNDS and CHALDEAN_NIGHT_BOUNDS in egyptian_bounds.py |
| Essential dignity enum | Domicile, exaltation, detriment, fall, peregrine | Triplicity, bound, face as first-class EssentialDignityKind members |
Implemented: TRIPLICITY, BOUND, FACE in EssentialDignityKind + scoring in dignities.py/longevity.py |
| Hayz | is_in_hayz() |
Source-correct Halb/Hayz provenance and Mercury evaluability | Implemented: named al-Qabisi/Bonatti doctrine, sect-relative Halb, gender-added Hayz, explicit Mercury truth |
| Planetary condition | Cazimi, combust, under the beams | Oriental/occidental (morning/evening star), besieging | Implemented: oriental_occidental(), is_besieged() + conditions |
| Aspect direction | Sinister/dexter exists in primary directions | Not in the general aspect engine | Partial: direction logic present; full general engine + overcoming needs confirmation |
| Feature | Doctrinal significance | Verification Status |
|---|---|---|
| Planetary joys | Foundational to Hellenistic house rationale | Implemented: PLANETARY_JOYS + is_in_joy() + JOY condition |
| Overcoming (katarchein) | The zodiacal-superiority doctrine | Not yet located in general code (timelords or aspects) |
| Whole-sign aspects | Sign-based aspect mode distinct from degree-based | Implemented: AspectDomain.WHOLE_SIGN + find_whole_sign_aspects() |
| Besieging (enclosure) | Malefic enclosure condition | Implemented: is_besieged() |
Admitted values:
The bounds doctrine axis determines which table is used for bound rulership lookup. All three systems assign the same five rulers (Mercury, Venus, Mars, Jupiter, Saturn) across the same 30-degree sign span, but in different segment arrangements.
Admitted values:
longevity.py)The triplicity scheme axis is relevant once triplicity becomes a first-class essential dignity rather than a scoring-only concept.
Admitted values:
Both modes yield the same five Ptolemaic aspects. The distinction is whether planets in the same sign are conjunct regardless of degree separation and whether a planet at 29 degrees Aries and 1 degree Leo form a trine.
Admitted values:
The oriental/occidental axis is relevant for planetary condition and accidental dignity scoring.
Verification note (2026-05-29): Per code review, the bulk of the tasks in Phases 1-4 below have been completed in the live code (see updated “Current Moira State” above and WESTERN_HELLENISTIC_GAP_TRACKER.md). The detailed task lists are retained for reference. Remaining work is now concentrated on confirmed gaps from FEATURE_AUDIT_2026.md (aphesis/distributions, Triacontaeteris, synastry depth, full ACG, etc.).
Scope: Promote triplicity, bound, and face to first-class essential dignity kinds, and implement planetary joys as an accidental condition.
Modules: moira/dignities.py, moira/longevity.py
Tasks:
Extend EssentialDignityKind with TRIPLICITY = "triplicity",
BOUND = "bound", FACE = "face".
Add scoring constants SCORE_TRIPLICITY = 3, SCORE_BOUND = 2,
SCORE_FACE = 1 to moira/dignities.py (migrated from longevity.py
inline values).
PLANETARY_JOYS lookup dict to moira/dignities.py:
PLANETARY_JOYS: dict[str, int] = {
"Mercury": 1, "Moon": 3, "Venus": 5,
"Mars": 6, "Sun": 9, "Jupiter": 11, "Saturn": 12,
}
Add is_in_joy(planet: str, house: int) -> bool function.
Add JOY = "joy" to AccidentalConditionKind with
SCORE_JOY = 3 and polarity STRENGTHENING.
Wire triplicity/bound/face detection into calculate_dignities() so the
result vessel reports the full five-level essential dignity state.
dignity_score_at() in longevity.py delegates to the same
constants rather than hardcoding weights.Acceptance criteria:
calculate_dignities(Body.JUPITER, lon=15.0, ...) for Jupiter at 15
degrees Aries reports TRIPLICITY as an essential dignity (Jupiter is
day triplicity ruler of fire signs in the Dorotheus scheme).is_in_joy("Mars", 6) returns True.is_in_joy("Mars", 5) returns False.Constitutional target: P1 for planetary joys; existing dignity subsystem remains at its current constitutional level.
Do not: Modify egyptian_bounds.py, aspects.py, or timelords.py
during this phase.
Scope: Add Ptolemaic and Chaldean term tables alongside the existing Egyptian bounds, and implement the halb condition.
Modules: moira/egyptian_bounds.py (renamed or generalized),
moira/dignities.py
Tasks:
PTOLEMAIC, CHALDEAN_DAY, and CHALDEAN_NIGHT are admitted alongside
the Egyptian default. The day/night split preserves the source-required
Saturn/Mercury sect ordering.
PTOLEMAIC_BOUNDS table (Tetrabiblos I.21):
Aries: Jupiter 0-6, Venus 6-14, Mercury 14-21, Mars 21-26, Saturn 26-30
Taurus: Venus 0-8, Mercury 8-15, Jupiter 15-22, Saturn 22-26, Mars 26-30
...
Add the source-derived CHALDEAN_DAY_BOUNDS and
CHALDEAN_NIGHT_BOUNDS tables from Ptolemy’s 8-7-6-5-4 construction.
EgyptianBoundsPolicy.__post_init__ accepts the four explicit doctrines
and rejects the former ambiguous chaldean value.
Route egyptian_bound_of() and bound_ruler() through the policy
doctrine to select the correct table.
Implement is_in_halb() in moira/dignities.py under the admitted
al-Qabisi/Bonatti doctrine. A diurnal planet requires the upper hemisphere
by day and lower hemisphere by night; a nocturnal planet requires the
inverse. Hayz adds placement in a sign of the planet’s own gender.
Add HALB = "halb" to AccidentalConditionKind with
SCORE_HALB = 1 and polarity STRENGTHENING.
IN_HALB to SectStateKind.Acceptance criteria:
egyptian_bound_of(15.0, policy=BoundsPolicy(doctrine=PTOLEMAIC))
returns Venus for 15 degrees Aries (Ptolemaic: Venus rules 6-14).egyptian_bound_of(15.0, policy=BoundsPolicy(doctrine=EGYPTIAN))
returns Mercury for 15 degrees Aries (Egyptian: Mercury rules 12-20).is_in_halb("Moon", ...) returns True for Moon above the horizon in a
night chart, independently of sign gender.is_in_hayz("Moon", ...) additionally requires a feminine sign.Constitutional target: P1 for Ptolemaic and Chaldean bounds.
Do not: Modify aspects.py or timelords.py during this phase.
Scope: Add oriental/occidental classification, besieging detection, and sinister/dexter aspect direction to the general aspect engine.
Modules: moira/dignities.py, moira/aspects.py
Tasks:
oriental_occidental(planet: str, planet_lon: float, sun_lon: float, planet_speed: float) -> str:
Add ORIENTAL = "oriental" and OCCIDENTAL = "occidental" to
AccidentalConditionKind with scores: SCORE_ORIENTAL = 2 (for
superior planets when oriental, inferior when occidental) and
SCORE_OCCIDENTAL = -2 (reverse).
is_besieged(planet_lon: float, chart_positions: dict, orb: float = 12.0) -> bool | tuple[str, str]:
Add BESIEGED = "besieged" to AccidentalConditionKind with
SCORE_BESIEGED = -5 and polarity WEAKENING.
AspectDirection(StrEnum) to moira/aspects.py:
class AspectDirection(StrEnum):
SINISTER = "sinister" # forward in zodiacal order
DEXTER = "dexter" # backward in zodiacal order
Add direction: AspectDirection | None field to AspectData.
Compute direction at detection time: if body1 is zodiacally behind body2, the aspect is dexter from body1’s perspective (the ray is cast backward).
overcoming(body1_lon: float, body2_lon: float) -> str | None:
Acceptance criteria:
Constitutional target: P1 for oriental/occidental, besieging, and aspect direction.
Do not: Modify egyptian_bounds.py, timelords.py, or house
computation during this phase.
Scope: Add a whole-sign (sign-based) aspect detection mode as an alternative geometry alongside degree-based detection.
Module: moira/aspects.py
Tasks:
Add WHOLE_SIGN = "whole_sign" to AspectDomain.
find_whole_sign_aspects(positions: dict) -> list[AspectData]:
Whole-sign aspects have no orb – they are either present or absent.
Set orb = 0.0 and exactness = 1.0 on the result vessel.
Whole-sign aspects still carry sinister/dexter direction (from Phase 3).
whole_sign: bool = False parameter to AspectPolicy to enable
whole-sign mode as an alternative to degree-based detection.Acceptance criteria:
Constitutional target: P1 for whole-sign aspects.
| Phase | Depends on | Touches |
|---|---|---|
| Phase 1 | None | dignities.py, longevity.py |
| Phase 2 | None | egyptian_bounds.py, dignities.py |
| Phase 3 | Phase 1 (for enum patterns) | dignities.py, aspects.py |
| Phase 4 | Phase 3 (sinister/dexter) | aspects.py |
Phases 1 and 2 are independent and can be implemented in parallel. Phase 3 requires Phase 1 for enum extension patterns. Phase 4 requires Phase 3 for aspect direction infrastructure.
test_dignities.py and test_longevity.py
tests must pass.test_egyptian_bounds.py tests must pass.test_aspects.py tests must pass.Should EgyptianBoundsDoctrine be renamed to BoundsDoctrine (or
TermsDoctrine) when Ptolemaic and Chaldean are added, or should the
Egyptian prefix be kept for backward compatibility?
Should dignity_score_at() remain in longevity.py or migrate to
dignities.py now that the scoring constants are being formalized there?
Should oriental/occidental for inferior planets (Mercury, Venus) use elongation-based classification (> 0 degrees elongation = morning star) or heliacal-event-based classification (has the planet had its last heliacal rising)?
Should whole-sign aspects produce the same AspectData vessel as
degree-based aspects, or a distinct WholeSignAspectData vessel?
What score should be assigned to overcoming? Classical sources treat it as a qualitative relational condition rather than a point-based dignity. It may belong in a relational layer rather than the accidental condition enum.
Should Dorotheus-style triplicity be the only scheme at Phase 1, or should the triplicity axis be parameterized from the start?
moira/dignities.py, moira/longevity.py,
moira/egyptian_bounds.py, moira/aspects.py.