Эх сурвалжийг харах

Phase 0-4: Natal chart bug fix, post-processing functions, aspect patterns, chart shape, expanded natal tool

- Fix: natal chart now passes speed_lon for applying/separating detection
- Add 14 post-processing functions to astrology.py (element/modality balance, stelliums, chart ruler, house rulers, groupings, retrograde list, nodal axis, Saturn info, Pluto polarity point, Part of Fortune, 12th house analysis, aspect filtering)
- Add aspect pattern detection (T-square, Grand Trine, Grand Cross, Yod) with quincunx aspect
- Add chart shape detection (bundle, bowl, bucket, splash, locomotive, seesaw, splay)
- Expand calculate_natal_chart with include_overview, include_patterns, include_karmic, top_n_aspects params
- Add lunar phase to natal chart output
- 162 tests passing (146 astrology + 16 storage)
Lukas Goldschmidt 1 сар өмнө
parent
commit
0dfa7d6f82

+ 428 - 0
IMPLEMENTATION_PLAN.md

@@ -0,0 +1,428 @@
+# Astro MCP Implementation Plan
+
+> Based on: WISHLIST_ANALYSIS.md
+> Strategy: Expand existing tools rather than creating new ones where possible.
+> Each phase is independently testable. Stop after each phase for review.
+
+---
+
+## Phase 0: Fix Natal Chart Applying/Separating Bug
+
+**File**: `src/astro_mcp/tools.py`, `calculate_natal_chart` function
+
+The natal chart tool builds planet lines without `speed_lon`, then calls `compute_aspects` on bodies that also lack `speed_lon`. This means `applying` is always `None` for natal aspects.
+
+**Change**: When building the `aspect_bodies` list in `calculate_natal_chart`, include `speed_lon` from the raw ephemeris data. This requires keeping a lookup of `speed_lon` by body name from the raw bodies.
+
+```python
+# Before (line ~170):
+aspect_bodies = [{"name": p["body"], "lon": p["absolute_lon"]} for p in planets]
+
+# After:
+speed_lookup = {b["body"]: b.get("speed_lon") for b in raw_bodies}
+aspect_bodies = [{"name": p["body"], "lon": p["absolute_lon"], "speed_lon": speed_lookup.get(p["body"])} for p in planets]
+```
+
+**Tests**: Add a test in `test_tools.py` that verifies `applying` is not `None` for natal chart aspects when planets have different speeds.
+
+**Effort**: ~5 lines changed.
+
+---
+
+## Phase 1: Chart Overview Post-Processing Functions
+
+**File**: `src/astro_mcp/astrology.py` (new functions)
+
+Add these pure functions that operate on the planet list returned by `calculate_natal_chart`:
+
+### 1.1 `get_element_balance(planets) -> dict`
+Count planets by element. Each sign maps to an element:
+- Fire: Aries, Leo, Sagittarius
+- Earth: Taurus, Virgo, Capricornus
+- Air: Gemini, Libra, Aquarius
+- Water: Cancer, Scorpius, Pisces
+
+Returns: `{"fire": N, "earth": N, "air": N, "water": N, "percentages": {...}}`
+
+### 1.2 `get_modality_balance(planets) -> dict`
+Count planets by modality:
+- Cardinal: Aries, Cancer, Libra, Capricornus
+- Fixed: Taurus, Leo, Scorpius, Aquarius
+- Mutable: Gemini, Virgo, Sagittarius, Pisces
+
+Returns: `{"cardinal": N, "fixed": N, "mutable": N, "percentages": {...}}`
+
+### 1.3 `get_hemisphere_emphasis(planets) -> dict`
+Count planets by house ranges:
+- Upper (houses 7-12), Lower (1-6), East (10-3), West (4-9)
+
+Returns: `{"upper": N, "lower": N, "east": N, "west": N}`
+
+### 1.4 `detect_stelliums(planets) -> list`
+Scan for 3+ planets in the same sign OR same house.
+
+Returns: `[{"type": "sign"|"house", "key": str, "planets": [str, ...]}, ...]`
+
+### 1.5 `get_empty_houses(planets) -> list[int]`
+Return house numbers (1-12) with no planets.
+
+### 1.6 `get_chart_ruler(ascendant_sign, planets) -> dict`
+Sign-to-ruler mapping:
+- Aries -> Mars, Taurus -> Venus, Gemini -> Mercury, Cancer -> Moon,
+- Leo -> Sun, Virgo -> Mercury, Libra -> Venus, Scorpius -> Pluto (traditional: Mars),
+- Sagittarius -> Jupiter, Capricornus -> Saturn, Aquarius -> Uranus (traditional: Saturn),
+- Pisces -> Neptune (traditional: Jupiter)
+
+Return the ruling planet's full data from the planets list.
+
+### 1.7 `get_house_rulers(houses, planets) -> list`
+For each house cusp sign, find the ruling planet and its condition.
+
+### 1.8 `group_planets_by_house(planets) -> dict`
+Returns: `{house_number: [planet_names]}`
+
+### 1.9 `group_planets_by_sign(planets) -> dict`
+Returns: `{sign_name: [planet_names]}`
+
+### 1.10 `get_house_type_counts(planets) -> dict`
+Angular (1,4,7,10), Succedent (2,5,8,11), Cadent (3,6,9,12).
+
+### 1.11 `get_retrograde_planets(planets) -> list`
+Filter planets where `retrograde=True`, return with sign + house.
+
+### 1.12 `get_nodal_axis(planets, houses) -> dict`
+Extract `true_node` from planets, compute South Node (opposite point).
+
+### 1.13 `get_saturn_info(planets) -> dict`
+Extract Saturn from planets list with sign, house, retrograde.
+
+### 1.14 `get_part_of_fortune(ascendant_lon, sun_lon, moon_lon) -> dict`
+Formula: `normalize_degrees(ascendant_lon + moon_lon - sun_lon)`. Return sign + degree.
+
+### 1.15 `get_pluto_polarity_point(pluto_lon) -> dict`
+Formula: `normalize_degrees(pluto_lon + 180)`. Return sign + degree.
+
+**Tests**: Add all functions to `test_astrology.py` with known inputs.
+
+**Effort**: ~200 lines in astrology.py, ~150 lines in test_astrology.py.
+
+---
+
+## Phase 2: Aspect Pattern Detection
+
+**File**: `src/astro_mcp/astrology.py`
+
+### 2.1 `detect_aspect_patterns(planets, aspects) -> list`
+
+Scan the aspect list for known patterns:
+
+**T-square**: Find an opposition (A-B). Check if any planet C forms a square to both A and B. Apex = C.
+
+**Grand Trine**: Find 3 planets where each pair is in trine (3 trines total). All 3 must be in the same element.
+
+**Grand Cross**: Find 4 planets forming 2 oppositions and 4 squares (each planet opposes one and squares two others).
+
+**Yod**: Find 2 planets in sextile. Check if any planet C forms a quincunx (150°) to both. Note: quincunx is not in the current aspect definitions -- add it first.
+
+**Add quincunx to ASPECT_DEFINITIONS**:
+```python
+{"name": "quincunx", "angle": 150.0, "default_orb": 3.0, "symbol": "Qx"}
+```
+
+Returns: `[{"type": "T-square"|"Grand Trine"|"Grand Cross"|"Yod", "planets": [...], "apex": str|null, "modality": str|null, "element": str|null}]`
+
+**Tests**: Create synthetic planet configurations that form each pattern type and verify detection.
+
+**Effort**: ~150 lines in astrology.py, ~100 lines in tests.
+
+---
+
+## Phase 3: Chart Shape Detection
+
+**File**: `src/astro_mcp/astrology.py`
+
+### 3.1 `detect_chart_shape(planets) -> dict`
+
+Analyze the angular distribution of planets to classify the chart shape:
+
+1. Sort planets by longitude
+2. Compute gaps between adjacent planets (including wraparound)
+3. Find the largest gap
+4. Classify:
+   - **Bundle**: All planets within 120° arc (largest gap >= 240°)
+   - **Bowl**: All planets within 180° arc (largest gap >= 180°)
+   - **Bucket**: All planets within 240° arc, with one planet opposite (singleton) -- largest gap < 240° but > 120°
+   - **Splash**: Planets distributed around the full 360° (largest gap < 120°)
+   - **Locomotive**: Planets within ~240° with a "locomotive" planet at the start of the empty arc
+   - **Seesaw**: Two clusters of planets roughly opposite each other
+   - **Splay**: Three or more planet pairs/groups distributed around the chart
+
+Returns: `{"shape": str, "largest_gap": float, "gap_start": float, "gap_end": float}`
+
+**Tests**: Create synthetic planet configurations for each shape.
+
+**Effort**: ~80 lines in astrology.py, ~80 lines in tests.
+
+---
+
+## Phase 4: Expand calculate_natal_chart with Overview
+
+**File**: `src/astro_mcp/tools.py`
+
+### 4.1 Add `include_overview` parameter to `calculate_natal_chart`
+
+```python
+async def calculate_natal_chart(
+    ...existing params...,
+    include_overview: bool = False,
+    include_patterns: bool = False,
+    include_karmic: bool = False,
+    top_n_aspects: int | None = None,
+) -> dict[str, Any]:
+```
+
+When `include_overview=True`, add an `"overview"` key to the output containing:
+- element_balance, modality_balance, hemisphere_emphasis
+- stelliums, empty_houses
+- chart_ruler, house_rulers
+- planets_by_house, planets_by_sign
+- house_type_counts
+- retrograde_planets
+- lunar_phase (from ephemeris `lunar_state`)
+
+When `include_patterns=True`, add an `"aspect_patterns"` key with T-square, Grand Trine, Grand Cross, Yod, and chart_shape.
+
+When `include_karmic=True`, add a `"karmic"` key with:
+- nodal_axis, saturn_info, pluto_polarity_point
+- nodal_aspects (filter aspects for true_node)
+- saturn_aspects (filter aspects for Saturn hard aspects to personal planets)
+- retrograde_planets (emphasize personal planet retrogrades)
+- 12th_house_analysis (cusp sign, planets, ruler)
+
+When `top_n_aspects` is set, limit the aspects list to the N tightest.
+
+**Tests**: Add tests in `test_tools.py` verifying each flag produces the expected output sections.
+
+**Effort**: ~100 lines in tools.py, ~150 lines in tests.
+
+---
+
+## Phase 5: Expand calculate_synastry_chart
+
+**File**: `src/astro_mcp/tools.py`
+
+### 5.1 Add parameters to `calculate_synastry_chart`
+
+```python
+async def calculate_synastry_chart(
+    ...existing params...,
+    top_n_aspects: int | None = None,
+    karmic_filter: bool = False,
+    significator_filter: bool = False,
+) -> dict[str, Any]:
+```
+
+- `top_n_aspects`: Limit interaspects to top N by orb
+- `karmic_filter`: Only return interaspects involving Saturn, Pluto, or true_node
+- `significator_filter`: Only return interaspects involving Venus-Mars, Moon-Venus, Sun-Moon, Sun-Saturn pairs
+
+Add a `"summary"` key to the output containing:
+- `top_aspects`: Top 10 interaspects by orb
+- `saturn_contacts`: All Saturn interchart aspects
+- `node_contacts`: All Node interchart aspects
+- `venus_mars_contacts`: All Venus-Mars interchart aspects
+- `sun_moon_contacts`: All Sun-Moon interchart aspects
+
+**Tests**: Add tests verifying filters and summary sections.
+
+**Effort**: ~60 lines in tools.py, ~100 lines in tests.
+
+---
+
+## Phase 6: Full Davison Chart Tool
+
+**File**: `src/astro_mcp/tools.py`
+
+### 6.1 New tool: `calculate_davison_chart`
+
+```python
+@mcp.tool()
+async def calculate_davison_chart(
+    person1_datetime: str,
+    person1_latitude: float,
+    person1_longitude: float,
+    person2_datetime: str,
+    person2_latitude: float,
+    person2_longitude: float,
+    elevation: float = 0.0,
+    house_system: str = "placidus",
+    orb_limits: dict[str, float] | None = None,
+) -> dict[str, Any]:
+```
+
+Algorithm:
+1. Compute Davison midpoint datetime (same as existing `compute_davison_chart`)
+2. Compute Davison midpoint lat/lon (average of both birth locations)
+3. Call `call_sky_state` at the Davison midpoint datetime + location
+4. Calculate houses from the Davison LST + latitude
+5. Build planet list with house placement
+6. Calculate aspects between Davison planets
+7. Calculate angles
+8. Return full chart structure (same format as `calculate_natal_chart`)
+
+### 6.2 New tool: `calculate_davison_chart_by_id`
+
+Same as above but accepts `person1_id` and `person2_id` from the database.
+
+### 6.3 Update `calculate_synastry_chart`
+
+Change the Davison section from returning just midpoint coordinates to returning the full chart. Or alternatively, keep the midpoint data and add a `"davison_chart_full"` key.
+
+**Recommendation**: Keep the existing midpoint data (for backward compatibility) and add a `include_davison_full: bool = False` parameter. When True, compute and include the full Davison chart.
+
+**Tests**: Add tests for Davison chart calculation with known birth data.
+
+**Effort**: ~150 lines in tools.py, ~100 lines in tests.
+
+---
+
+## Phase 7: Transit Tools for Composite/Davison
+
+**File**: `src/astro_mcp/tools.py`
+
+### 7.1 New tool: `get_composite_transit_preview`
+
+```python
+@mcp.tool()
+async def get_composite_transit_preview(
+    person1_datetime: str,
+    person1_latitude: float,
+    person1_longitude: float,
+    person2_datetime: str,
+    person2_latitude: float,
+    person2_longitude: float,
+    start_date: str,
+    end_date: str,
+    min_significance: float = 0.0,
+) -> dict[str, Any]:
+```
+
+Algorithm:
+1. Calculate composite chart (reuse `calculate_composite_chart`)
+2. For each day in range, get transiting planet positions
+3. Compute aspects between transiting planets and composite planet positions
+4. Return daily snapshots (same format as `get_transit_preview`)
+
+### 7.2 New tool: `get_davison_transit_preview`
+
+Same pattern but using Davison chart positions as the "natal" targets.
+
+### 7.3 `_byId` variants
+
+Add `get_composite_transit_preview_by_id` and `get_davison_transit_preview_by_id`.
+
+**Tests**: Add tests for composite and Davison transit previews.
+
+**Effort**: ~150 lines in tools.py, ~100 lines in tests.
+
+---
+
+## Phase 8: Return Type Updates
+
+**File**: `src/astro_mcp/tools.py`
+
+### 8.1 Update `_tool_names()`
+
+Add all new tools to the `_tool_names()` function in `server.py`:
+- `calculate_davison_chart`
+- `calculate_davison_chart_by_id`
+- `get_composite_transit_preview`
+- `get_composite_transit_preview_by_id`
+- `get_davison_transit_preview`
+- `get_davison_transit_preview_by_id`
+
+### 8.2 Update root endpoint test
+
+Update `test_root_lists_tools` in `test_server.py` to include the new tool names.
+
+---
+
+## Phase 9: Karmic Relationship Synthesis
+
+**File**: `src/astro_mcp/tools.py`
+
+### 9.1 New tool: `get_karmic_relationship_summary`
+
+```python
+@mcp.tool()
+async def get_karmic_relationship_summary(
+    person1_id: str,
+    person2_id: str,
+    house_system: str = "placidus",
+) -> dict[str, Any]:
+```
+
+Algorithm:
+1. Get synastry chart (with karmic_filter=True)
+2. Get composite chart
+3. Get Davison chart
+4. Extract karmic indicators:
+   - Saturn-Node contacts (synastry)
+   - Pluto-Node contacts (synastry)
+   - Node conjunctions (synastry)
+   - Saturn personal planet contacts (synastry)
+   - Composite Saturn/Pluto/Node positions
+   - Davison Saturn/Pluto/Node positions
+5. Return structured summary with karmic weight assessment
+
+**Effort**: ~100 lines in tools.py, ~50 lines in tests.
+
+---
+
+## Phase 10: Nice-to-Have (Deferred)
+
+These are lower priority and can be done after the above phases are complete:
+
+1. **Vertex axis** (~25 lines in astrology.py)
+2. **Fixed star conjunctions** (~100 lines + star catalog data)
+3. **Coalescent chart** (~150 lines, complex algorithm)
+4. **Relationship type classifier** (~80 lines, heuristic)
+5. **Compatibility scoring** (~100 lines, heuristic)
+6. **Eclipse proximity** (needs external eclipse data source)
+7. **Planetary nodes** (needs specialized ephemeris data)
+
+---
+
+## Summary of Changes
+
+| Phase | Files Changed | New Lines (approx) | New Tools |
+|---|---|---|---|
+| 0: Bug fix | tools.py | ~5 | 0 |
+| 1: Post-processing | astrology.py, test_astrology.py | ~350 | 0 |
+| 2: Aspect patterns | astrology.py, test_astrology.py | ~250 | 0 |
+| 3: Chart shape | astrology.py, test_astrology.py | ~160 | 0 |
+| 4: Natal overview | tools.py, test_tools.py | ~250 | 0 (params) |
+| 5: Synastry filters | tools.py, test_tools.py | ~160 | 0 (params) |
+| 6: Davison chart | tools.py, test_tools.py, server.py | ~250 | 2 |
+| 7: Composite/Davison transits | tools.py, test_tools.py, server.py | ~250 | 4 |
+| 8: Return types | server.py, test_server.py | ~20 | 0 |
+| 9: Karmic synthesis | tools.py, test_tools.py | ~150 | 1 |
+| **Total** | | **~1,845** | **7** |
+
+---
+
+## Testing Strategy
+
+- All new astrology functions get unit tests in `test_astrology.py`
+- All new tool behaviors get integration tests in `test_tools.py` (with mocked ephemeris)
+- The existing 103 tests must continue to pass
+- Run `pytest tests/` after each phase
+
+## Verification Checklist
+
+After all phases:
+- [ ] `pytest tests/` passes with no failures
+- [ ] All new functions have type annotations
+- [ ] All new tools appear in `GET /` output
+- [ ] All new tools have docstrings
+- [ ] No breaking changes to existing tool signatures (new params have defaults)

+ 475 - 0
WISHLIST_ANALYSIS.md

@@ -0,0 +1,475 @@
+# Astro MCP Wishlist Analysis
+
+> Generated: 2026-06-04
+> Analyzed against: src/astro_mcp/astrology.py, src/astro_mcp/tools.py, tests/
+
+---
+
+## 1. NATAL ASTROLOGY WISHLIST
+
+### 1.1 Already Implemented (confirmed in code)
+
+| Wishlist Item | Status | Location |
+|---|---|---|
+| Natal chart calculation (planets, houses, angles, aspects) | DONE | `calculate_natal_chart` in tools.py |
+| Planetary positions with sign, degree, retrograde | DONE | `get_planetary_positions`, `calculate_natal_chart` |
+| House systems (Placidus, Equal, Whole Sign) | DONE | `calculate_houses` in astrology.py |
+| Aspect calculation (conj, opp, sq, tr, sx) with orbs | DONE | `compute_aspects` in astrology.py |
+| Person database (store/retrieve birth data) | DONE | `person_manage` in tools.py |
+| Transit chart calculation | DONE | `calculate_transit_chart` in tools.py |
+| Transit preview (daily snapshots + significance) | DONE | `get_transit_preview` in tools.py |
+| Lunar nodes (North/South) | DONE | `true_node` body from ephemeris |
+| Synastry chart | DONE | `calculate_synastry_chart` in tools.py |
+| Composite chart | DONE | `calculate_composite_chart` in tools.py |
+| Multiple birth data formats (ISO 8601 with TZ) | DONE | All tools accept ISO 8601 |
+| Applying/separating flag on aspects | PARTIAL | Computed in transit chart; natal chart does NOT pass speed_lon to compute_aspects |
+
+### 1.2 Missing -- Easy Wins (post-processing of existing data)
+
+#### 1.2.1 Element Balance Report (High Priority)
+- **What**: Count planets by element (fire/earth/air/water) with percentages
+- **Effort**: Trivial -- bucket the 12 planets by sign element
+- **Where**: New function `get_element_balance(planets)` in astrology.py (~15 lines)
+- **Integration**: Add `include_overview: bool` param to `calculate_natal_chart`, or new tool `get_chart_overview`
+
+#### 1.2.2 Modality Balance Report (High Priority)
+- **What**: Count planets by modality (cardinal/fixed/mutable) with percentages
+- **Effort**: Trivial -- bucket by sign modality
+- **Where**: New function `get_modality_balance(planets)` in astrology.py (~15 lines)
+
+#### 1.2.3 Hemisphere Emphasis (Medium Priority)
+- **What**: Report which hemisphere (upper/lower/east/west) has most planets
+- **Effort**: Trivial -- count planets by house ranges (upper=7-12, lower=1-6, etc.)
+- **Where**: New function `get_hemisphere_emphasis(planets)` in astrology.py (~15 lines)
+
+#### 1.2.4 Stellium Detection (High Priority)
+- **What**: Flag any sign or house with 3+ planets
+- **Effort**: Trivial -- scan planet list for sign/house clusters
+- **Where**: New function `detect_stelliums(planets)` in astrology.py (~20 lines)
+
+#### 1.2.5 Empty House List (Low Priority)
+- **What**: Report which houses have no planets
+- **Effort**: Trivial -- invert house placement list
+- **Where**: New function `get_empty_houses(planets)` in astrology.py (~10 lines)
+
+#### 1.2.6 Chart Ruler Identification (High Priority)
+- **What**: Return the planet ruling the Ascendant sign, with its sign/house/aspects
+- **Effort**: Trivial -- sign-to-ruler lookup table (Aries->Mars, Taurus->Venus, etc.), then find that planet in the list
+- **Where**: New function `get_chart_ruler(angles, planets)` in astrology.py (~15 lines)
+
+#### 1.2.7 Sun-Moon Aspect (Medium Priority)
+- **What**: Specifically return the aspect (if any) between Sun and Moon
+- **Effort**: Trivial -- filter existing aspect list for Sun-Moon pair
+- **Where**: Filter on `calculate_natal_chart` output, or param `include_big_three_aspects`
+
+#### 1.2.8 Top Aspects by Orb (High Priority)
+- **What**: Return the N tightest aspects (smallest orbs)
+- **Effort**: Trivial -- aspects already sorted by orb; just add `top_n` parameter
+- **Where**: Add `top_n: int | None` param to `calculate_natal_chart`
+
+#### 1.2.9 Aspects to Big Three (High Priority)
+- **What**: Return all aspects involving Sun, Moon, or Ascendant
+- **Effort**: Trivial -- filter aspect list
+- **Where**: Add `filter_to_big_three: bool` param or post-processing function
+
+#### 1.2.10 Aspects to Chart Ruler (Medium Priority)
+- **What**: Return all aspects involving the chart ruler
+- **Effort**: Trivial -- filter after identifying ruler
+- **Where**: Same as above, chained
+
+#### 1.2.11 House Cusp Signs (High Priority)
+- **What**: Return the sign on each house cusp
+- **Effort**: Already returned in `houses` array (each house has `sign`, `degree`)
+- **Status**: Already done, just needs better documentation
+
+#### 1.2.12 House Rulers (Medium Priority)
+- **What**: For each house, return the ruling planet and its sign/house/condition
+- **Effort**: Trivial -- sign-to-ruler lookup + planet lookup
+- **Where**: New function `get_house_rulers(houses, planets)` in astrology.py (~25 lines)
+
+#### 1.2.13 Planets in Houses Grouping (High Priority)
+- **What**: Group planets by house for quick house-themed reading
+- **Effort**: Trivial -- each planet already has `house` field; just group
+- **Where**: New function `group_planets_by_house(planets)` in astrology.py (~10 lines)
+
+#### 1.2.14 Planets in Signs Grouping (High Priority)
+- **What**: Group planets by sign for quick sign-themed reading
+- **Effort**: Trivial -- each planet already has `sign` field; just group
+- **Where**: New function `group_planets_by_sign(planets)` in astrology.py (~10 lines)
+
+#### 1.2.15 Angular/Succedent/Cadent Count (Medium Priority)
+- **What**: Count planets by house type
+- **Effort**: Trivial -- angular={1,4,7,10}, succedent={2,5,8,11}, cadent={3,6,9,12}
+- **Where**: New function `get_house_type_counts(planets)` in astrology.py (~15 lines)
+
+#### 1.2.16 Retrograde Planet List (Medium Priority)
+- **What**: Return all retrograde planets with sign/house
+- **Effort**: Trivial -- filter planet list where `retrograde=True`
+- **Where**: New function `get_retrograde_planets(planets)` in astrology.py (~10 lines)
+
+#### 1.2.17 Retrograde Emphasis Flag (Low Priority)
+- **What**: Flag charts with 3+ retrograde planets
+- **Effort**: Trivial -- count from retrograde list
+
+#### 1.2.18 Sun/Moon Phase (Low Priority)
+- **What**: Return the lunar phase at birth
+- **Effort**: Ephemeris already returns `lunar_state.phase_name`; just expose it
+- **Where**: Add to `calculate_natal_chart` output
+
+#### 1.2.19 Applying/Separation Fix for Natal Chart (Medium Priority)
+- **What**: The natal chart tool does NOT pass `speed_lon` to `compute_aspects`, so `applying` is always `None`
+- **Effort**: Small fix -- pass speed_lon through in the aspect computation loop
+- **Where**: `calculate_natal_chart` in tools.py, ~line 170
+
+### 1.3 Missing -- New Algorithms Needed
+
+#### 1.3.1 Chart Shape Detection (Medium Priority)
+- **What**: Identify bundle, bowl, bucket, splash, locomotive, seesaw, splay patterns
+- **Effort**: Moderate -- analyze angular distribution of planets
+- **Algorithm**: Compute the largest unoccupied arc; classify based on planet clustering
+- **Where**: New function `detect_chart_shape(planets)` in astrology.py (~80 lines)
+
+#### 1.3.2 Aspect Pattern Detection (High Priority)
+- **What**: T-square, Grand Trine, Grand Cross, Yod detection
+- **Effort**: Moderate -- graph topology analysis on aspect list
+- **Algorithm**:
+  - T-square: find opposition pair, check if both square a common planet
+  - Grand trine: find 3 planets all in trine (same element)
+  - Grand cross: find 4 planets forming 2 oppositions + 4 squares
+  - Yod: find 2 sextile planets both quincunx a third
+- **Where**: New function `detect_aspect_patterns(aspects)` in astrology.py (~150 lines)
+
+#### 1.3.3 Saturn/Jupiter Return Flags (Medium Priority)
+- **What**: Flag if person is currently in Saturn return (~28-30) or Jupiter return (~12 years)
+- **Effort**: Moderate -- compute approximate return dates from birth date + orbital period, or scan transits
+- **Where**: New function `get_return_dates(birth_datetime, planet)` in astrology.py (~40 lines)
+
+#### 1.3.4 Eclipse Proximity (Low Priority)
+- **What**: Flag if any natal planet is within 5° of current eclipse axis
+- **Effort**: Needs eclipse data source not currently in ephemeris
+- **Status**: Deferred -- requires external data
+
+---
+
+## 2. KARMIC ASTROLOGY WISHLIST
+
+### 2.1 Already Implemented (confirmed)
+
+| Wishlist Item | Status | Location |
+|---|---|---|
+| Natal chart calculation | DONE | `calculate_natal_chart` |
+| Lunar nodes (North/South) | DONE | `true_node` in ephemeris |
+| Planetary positions with retrograde | DONE | `calculate_natal_chart` |
+| Aspects between planets | DONE | `compute_aspects` |
+| House systems | DONE | `calculate_houses` |
+| Transit chart | DONE | `calculate_transit_chart` |
+| Synastry chart | DONE | `calculate_synastry_chart` |
+| Composite chart | DONE | `calculate_composite_chart` |
+| Person database | DONE | `person_manage` |
+
+### 2.2 Missing -- Easy Wins (post-processing / filtering)
+
+#### 2.2.1 Node Sign/House Lookup (High Priority)
+- **What**: Return North & South Node sign, house, degree
+- **Effort**: Trivial -- `true_node` is already in planet list; South Node = opposite point
+- **Where**: New function `get_nodal_axis(planets, houses)` in astrology.py (~10 lines)
+
+#### 2.2.2 Node Axis Aspects (High Priority)
+- **What**: Identify all aspects from natal planets to the nodal axis
+- **Effort**: Trivial -- filter existing aspects for `true_node` involvement
+- **Where**: Filter on `calculate_natal_chart` output, or `include_nodal_aspects` param
+
+#### 2.2.3 Node Conjunct Planets (High Priority)
+- **What**: Flag planets conjunct South Node (past-life gifts) and North Node (growth)
+- **Effort**: Trivial -- filter node axis aspects for conjunction
+- **Where**: Post-processing of nodal aspects
+
+#### 2.2.4 Node Square Planets (Medium Priority)
+- **What**: Flag planets square the nodal axis ("skipped steps")
+- **Effort**: Trivial -- filter node axis aspects for square
+
+#### 2.2.5 Nodal Axis by House (High Priority)
+- **What**: Return which house the nodal axis falls in
+- **Effort**: Trivial -- `true_node` already has `house` field
+
+#### 2.2.6 Saturn Sign/House (High Priority)
+- **What**: Return Saturn's sign, house, degree, retrograde status
+- **Effort**: Trivial -- Saturn is already in planet list
+- **Where**: New function `get_saturn_info(planets)` in astrology.py (~10 lines)
+
+#### 2.2.7 Saturn Aspects to Personal Planets (High Priority)
+- **What**: Identify Saturn hard aspects to Sun, Moon, Venus, Mars
+- **Effort**: Trivial -- filter aspect list for Saturn + personal planet pairs with hard aspects
+- **Where**: Filter on `calculate_natal_chart` output
+
+#### 2.2.8 Saturn Retrograde Flag (Medium Priority)
+- **What**: Flag Saturn retrograde
+- **Effort**: Trivial -- check Saturn's `retrograde` flag
+
+#### 2.2.9 Saturn-Node Aspects (High Priority)
+- **What**: Identify aspects between Saturn and the nodal axis
+- **Effort**: Trivial -- filter aspect list for Saturn-true_node pairs
+
+#### 2.2.10 Retrograde Planet List (High Priority)
+- **What**: Return all retrograde planets with signs/houses
+- **Effort**: Same as natal wishlist item 1.2.16
+
+#### 2.2.11 12th House Cusp Sign (Medium Priority)
+- **What**: Return the sign on the 12th house cusp
+- **Effort**: Trivial -- `houses[11]` already has this
+
+#### 2.2.12 12th House Planets (Medium Priority)
+- **What**: List any planets in the 12th house
+- **Effort**: Trivial -- filter planets where `house == 12`
+
+#### 2.2.13 12th House Ruler (Low Priority)
+- **What**: Identify the ruler of the 12th house and its sign/house/aspects
+- **Effort**: Trivial -- sign ruler lookup + planet lookup
+
+#### 2.2.14 Karmic Synastry Filters (High Priority)
+- **What**: Saturn-Node, Pluto-Node, Node conjunctions in synastry
+- **Effort**: Trivial -- filter existing interaspects for these pairs
+- **Where**: Add `karmic_filter: bool` param to `calculate_synastry_chart`
+
+#### 2.2.15 Composite Node/Saturn/Pluto (Medium Priority)
+- **What**: Return composite chart nodal axis, Saturn, Pluto
+- **Effort**: Trivial -- already in composite planet list; just surface prominently
+
+#### 2.2.16 Transit Triggers for Karmic Periods (High Priority)
+- **What**: Saturn/Pluto transits to natal nodes
+- **Effort**: Extend `get_transit_preview` with `karmic_only: bool` filter
+
+### 2.3 Missing -- New Algorithms Needed
+
+#### 2.3.1 Pluto Polarity Point (PPP) (High Priority)
+- **What**: Calculate the point opposite Pluto (sign, house, degree)
+- **Effort**: Trivial -- `normalize_degrees(pluto_lon + 180)`
+- **Where**: New function `get_pluto_polarity_point(planets, houses)` in astrology.py (~10 lines)
+
+#### 2.3.2 Saturn Return Timing (Medium Priority)
+- **What**: Calculate current/past/future Saturn return dates
+- **Effort**: Moderate -- Saturn orbital period ~29.5 years; compute from birth date
+- **Where**: New function `get_saturn_return_dates(birth_datetime)` in astrology.py (~30 lines)
+
+#### 2.3.3 Node Return Timing (Medium Priority)
+- **What**: Calculate when transiting nodal axis returns to natal position (~18.6 year cycle)
+- **Effort**: Moderate -- nodal period ~18.6 years
+- **Where**: New function `get_node_return_dates(birth_datetime)` in astrology.py (~20 lines)
+
+### 2.4 Nice-to-Have (New Tools)
+
+#### 2.4.1 Part of Fortune (Low Priority)
+- **What**: Arabic Part of Fortune -- sign, house, aspects
+- **Effort**: Trivial -- formula: ASC + Moon - Sun (in degrees)
+- **Where**: New function `get_part_of_fortune(angles, planets)` in astrology.py (~10 lines)
+
+#### 2.4.2 Chiron Position (Low Priority)
+- **What**: Chiron sign, house, aspects
+- **Effort**: Trivial -- `chiron` is already in the ephemeris mock data; confirm real ephemeris returns it
+- **Status**: Likely already available, needs verification
+
+#### 2.4.3 Vertex Axis (Low Priority)
+- **What**: Vertex sign/house (fated encounters)
+- **Effort**: Moderate -- Vertex = intersection of ecliptic with prime vertical, requires astronomical calculation
+- **Where**: New function `calculate_vertex(lst, latitude)` in astrology.py (~25 lines)
+
+#### 2.4.4 Fixed Star Conjunctions (Low Priority)
+- **What**: Major fixed stars conjunct natal planets
+- **Effort**: Moderate -- need fixed star catalog (Aldebaran, Regulus, Spica, Antares, etc.) + conjunction check
+- **Where**: New data file + new function in astrology.py (~100 lines)
+
+#### 2.4.5 Planetary Nodes (Low Priority)
+- **What**: Nodes of Pluto, Saturn, etc. (Jeffrey Wolf Green's evolutionary astrology)
+- **Effort**: Complex -- requires specialized astronomical data not in standard ephemeris
+- **Status**: Deferred
+
+---
+
+## 3. RELATIONSHIP ASTROLOGY WISHLIST
+
+### 3.1 Already Implemented (confirmed)
+
+| Wishlist Item | Status | Location |
+|---|---|---|
+| Natal chart calculation | DONE | `calculate_natal_chart` |
+| Synastry chart (interchart aspects, house overlays) | DONE | `calculate_synastry_chart` |
+| Composite chart calculation | DONE | `calculate_composite_chart` |
+| Person database | DONE | `person_manage` |
+| Aspect calculation with configurable orbs | DONE | `compute_aspects` |
+| House systems | DONE | `calculate_houses` |
+| Transit chart calculation | DONE | `calculate_transit_chart` |
+
+### 3.2 Missing -- Easy Wins (post-processing / filtering)
+
+#### 3.2.1 Interchart Aspects Matrix (High Priority)
+- **What**: Full matrix of aspects between all planets of Person A and Person B
+- **Effort**: Already computed as `interaspects` in `calculate_synastry_chart`
+- **Status**: Done, just needs better surfacing
+
+#### 3.2.2 House Overlay Report (High Priority)
+- **What**: For each planet in Chart A, report which house it falls in Chart B (and vice versa)
+- **Effort**: Already computed as `house_overlays` in `calculate_synastry_chart`
+- **Status**: Done
+
+#### 3.2.3 Top Synastry Aspects (High Priority)
+- **What**: Return the 10-15 tightest/most significant interchart aspects
+- **Effort**: Trivial -- interaspects already sorted by orb; add `top_n` param
+
+#### 3.2.4 Relationship Significator Aspects (High Priority)
+- **What**: Venus-Mars, Moon-Venus, Sun-Moon, Sun-Saturn interchart aspects
+- **Effort**: Trivial -- filter interaspects for these planet pairs
+- **Where**: Add `significator_filter: bool` param to `calculate_synastry_chart`
+
+#### 3.2.5 Saturn Synastry Aspects (High Priority)
+- **What**: Flag all Saturn interchart aspects to personal planets and nodes
+- **Effort**: Trivial -- filter for Saturn as person1_planet or person2_planet
+
+#### 3.2.6 Node Synastry Aspects (High Priority)
+- **What**: Flag all interchart aspects to the nodal axes
+- **Effort**: Trivial -- filter for `true_node` involvement
+
+### 3.3 Missing -- New Tools Needed
+
+#### 3.3.1 Full Davison Chart Calculation (High Priority)
+- **What**: Calculate the midpoint-in-time-and-space chart with full planetary positions, houses, angles, aspects
+- **Current state**: `calculate_synastry_chart` returns Davison midpoint coordinates (date_jd, lat, lon) but does NOT compute planets, houses, or aspects for the Davison chart
+- **Effort**: Moderate -- compute sky state at Davison midpoint datetime + location, then calculate full chart
+- **Where**: New tool `calculate_davison_chart` in tools.py, reusing `calculate_natal_chart` logic (~150 lines)
+
+#### 3.3.2 Davison Transit Preview (Medium Priority)
+- **What**: Transit-to-Davison chart aspect snapshots over a date range
+- **Effort**: Moderate -- reuse `get_transit_preview` with Davison chart positions
+- **Where**: New tool `get_davison_transit_preview` in tools.py (~80 lines)
+
+#### 3.3.3 Composite Transit Report (Medium Priority)
+- **What**: Transits to composite chart planets/angles for timing
+- **Effort**: Moderate -- reuse `get_transit_preview` with composite planet positions
+- **Where**: New tool `get_composite_transit_preview` in tools.py (~80 lines)
+
+#### 3.3.4 Composite Enhanced Interpretation (Medium Priority)
+- **What**: Composite planet meanings, angular planets, unaspected planets, house emphasis, chart ruler
+- **Effort**: Moderate -- post-processing of existing composite chart data
+- **Where**: New function `analyze_composite_chart(composite_data)` in astrology.py (~100 lines)
+
+#### 3.3.5 Karmic Relationship Synthesis (Medium Priority)
+- **What**: Combine synastry + composite + Davison karmic indicators into structured report
+- **Effort**: Moderate -- aggregate filtering across all three chart types
+- **Where**: New tool `get_karmic_relationship_summary` in tools.py (~100 lines)
+
+### 3.4 Nice-to-Have (Complex / Low Priority)
+
+#### 3.4.1 Coalescent Chart (Low Priority)
+- **What**: Lawrence Grinnell's harmonic coalescent method
+- **Effort**: Complex -- compute harmonic numbers from planet pair arcs
+- **Where**: New algorithm in astrology.py (~150 lines)
+
+#### 3.4.2 Relationship Type Classifier (Low Priority)
+- **What**: Classify relationship type (romantic, business, family, friendship, karmic)
+- **Effort**: Heuristic scoring based on aspect patterns
+- **Where**: New function in astrology.py (~80 lines)
+
+#### 3.4.3 Compatibility Score (Low Priority)
+- **What**: Category scores (emotional, sexual, intellectual, commitment, spiritual)
+- **Effort**: Heuristic weighting of aspect types
+- **Where**: New function in astrology.py (~100 lines)
+
+#### 3.4.4 Synthesis Transit Preview (Low Priority)
+- **What**: Combine transit-to-natal (both people) + transit-to-composite/Davison
+- **Effort**: Aggregation of existing transit data
+- **Where**: New tool combining existing transit previews (~60 lines)
+
+#### 3.4.5 Eclipse Activation of Relationship Charts (Low Priority)
+- **What**: Flag eclipses hitting composite/Davison angles or personal planets
+- **Effort**: Needs eclipse data source
+- **Status**: Deferred
+
+---
+
+## 4. CONSOLIDATED PRIORITY MATRIX
+
+### Tier 1: Post-Processing Only (Easiest -- add params to existing tools)
+Pure filtering/bucketing of data already returned by existing tools.
+
+| # | Feature | Wishlist(s) | Effort |
+|---|---|---|---|
+| 1 | Element/Modality/Hemisphere balance | Natal | ~45 lines |
+| 2 | Stellium detection | Natal | ~20 lines |
+| 3 | Chart ruler identification | Natal | ~15 lines |
+| 4 | Top aspects by orb | Natal | param only |
+| 5 | Aspects to Big Three / chart ruler | Natal | param only |
+| 6 | House rulers | Natal | ~25 lines |
+| 7 | Planets in houses/signs grouping | Natal | ~20 lines |
+| 8 | Angular/succedent/cadent count | Natal | ~15 lines |
+| 9 | Retrograde planet list | Natal, Karmic | ~10 lines |
+| 10 | Node axis aspects | Karmic | param only |
+| 11 | Saturn aspects to personal planets | Karmic | param only |
+| 12 | Saturn-Node aspects | Karmic | param only |
+| 13 | 12th house analysis | Karmic | ~15 lines |
+| 14 | Top synastry aspects | Relationship | param only |
+| 15 | Relationship significator aspects | Relationship | param only |
+| 16 | Saturn/Node synastry filtering | Relationship | param only |
+| 17 | Lunar phase at birth | Natal | expose existing |
+| 18 | Applying/separating fix for natal | Natal | bug fix |
+
+### Tier 2: New Functions in astrology.py (Moderate -- pure math)
+New algorithms that operate on existing data structures.
+
+| # | Feature | Wishlist(s) | Effort |
+|---|---|---|---|
+| 19 | Chart shape detection | Natal | ~80 lines |
+| 20 | Aspect pattern detection | Natal | ~150 lines |
+| 21 | Pluto Polarity Point | Karmic | ~10 lines |
+| 22 | Part of Fortune | Karmic | ~10 lines |
+| 23 | Vertex axis | Karmic | ~25 lines |
+| 24 | Saturn return dates | Natal, Karmic | ~30 lines |
+| 25 | Node return dates | Karmic | ~20 lines |
+| 26 | Composite enhanced analysis | Relationship | ~100 lines |
+
+### Tier 3: New MCP Tools (More involved -- require ephemeris calls)
+New tools that make additional ephemeris calls.
+
+| # | Feature | Wishlist(s) | Effort |
+|---|---|---|---|
+| 27 | Full Davison chart | Relationship | ~150 lines |
+| 28 | Composite transit preview | Relationship | ~80 lines |
+| 29 | Davison transit preview | Relationship | ~80 lines |
+| 30 | Karmic relationship synthesis | Relationship, Karmic | ~100 lines |
+
+### Tier 4: Nice-to-Have (Complex or needs external data)
+
+| # | Feature | Wishlist(s) | Effort |
+|---|---|---|---|
+| 31 | Coalescent chart | Relationship | ~150 lines |
+| 32 | Fixed star conjunctions | Karmic | ~100 lines + data |
+| 33 | Relationship type classifier | Relationship | ~80 lines |
+| 34 | Compatibility scoring | Relationship | ~100 lines |
+| 35 | Eclipse proximity/activation | Natal, Relationship | needs data source |
+| 36 | Planetary nodes | Karmic | needs special data |
+
+---
+
+## 5. KEY ARCHITECTURAL RECOMMENDATIONS
+
+### 5.1 Consolidate Post-Processing into a Chart Overview Tool
+Rather than adding 15 boolean params to `calculate_natal_chart`, create a single new tool:
+
+```
+get_chart_overview(person_id_or_birth_data, include_karmic=false, include_patterns=false)
+```
+
+This tool would call `calculate_natal_chart` internally, then run all post-processing functions on the result. Keeps the base tool clean and provides a rich analysis layer on top.
+
+### 5.2 Add Analysis Params to Synastry
+Add optional params to `calculate_synastry_chart`:
+- `top_n_aspects: int | None` -- limit interaspects to top N by orb
+- `karmic_filter: bool` -- only return Saturn/Pluto/Node interaspects
+- `significator_filter: bool` -- only return Venus-Mars, Moon-Venus, Sun-Moon, Sun-Saturn
+
+### 5.3 Fix Natal Chart Applying/Separating
+The natal chart tool should pass `speed_lon` to `compute_aspects` so the `applying` field is populated. This is a bug/omission, not a new feature.
+
+### 5.4 Davison Chart is the Biggest Gap
+The synastry tool returns Davison midpoint coordinates but not a full chart. A `calculate_davison_chart` tool is the highest-priority new tool -- it reuses existing ephemeris + astrology logic with the midpoint datetime/location.
+
+### 5.5 Agent Guides Already Exist
+The `agent-guides/` directory already contains detailed interpretation guides for natal, karmic, and relationship astrology. These guides are consumed by agents, not by the MCP server. The MCP server's job is to provide the raw computational data; the guides tell agents how to interpret it. This analysis focuses on the computational layer only.

+ 216 - 0
agent-guides/karmic-astrology.md

@@ -0,0 +1,216 @@
+# Karmic Astrology — Agent Interpretation Guide
+
+> For agents using the astro MCP to perform karmic chart interpretation.
+
+## Framework
+
+Karmic astrology interprets the natal chart as a map of the soul's journey across lifetimes. The core principle: **the chart shows karmic conditions, not fixed fate**. Free will operates in how one responds to these conditions.
+
+### Reading Order for Karmic Analysis
+
+1. **Lunar Nodes by sign and house** — the backbone of karmic interpretation
+2. **Saturn by sign, house, and aspects** — karma, responsibility, lessons
+3. **Pluto by sign and house, plus Pluto's Polarity Point** — evolutionary astrology layer
+4. **Retrograde planets** — unfinished business from past lives
+5. **12th house and its ruler** — spiritual karma, unconscious patterns
+6. **Key aspect patterns** — Node squares, Saturn-Node contacts, Pluto-South Node
+7. **Transits to the nodal axis** — timing of karmic activations
+
+---
+
+## Lunar Nodes: The Karmic Axis
+
+### South Node — Past Life Patterns
+
+The South Node represents:
+- Overdeveloped tendencies from previous incarnations
+- The comfort zone that can hold you back
+- Innate talents and skills (the "easy mode")
+- Emotional patterns and habits that feel familiar but limiting
+
+**Interpretation approach**: Study the South Node sign first as the karmic inheritance — strengths turned into limitations, automatic reactions, comfort zones.
+
+### North Node — Evolutionary Direction
+
+The North Node represents:
+- Qualities the soul is now trying to develop
+- The unfamiliar but necessary growth path
+- Present-life intention and soul's "to-do list"
+
+**Interpretation approach**: Clarify the North Node sign as the developmental path — qualities and life areas that feel challenging but are essential for evolution.
+
+### The Nodes as an Axis
+
+- The soul is in **transition from South Node patterns to North Node development**
+- Not "abandoning" one sign for the other, but **re-balancing both**
+- The house shows *where* the karmic drama plays out
+
+### Node Pairs by Sign — Quick Reference
+
+| North Node | South Node | Core Theme |
+|---|---|---|
+| Aries | Libra | Independence vs. over-cooperation |
+| Taurus | Scorpio | Stability vs. crisis-seeking |
+| Gemini | Sagittarius | Curiosity vs. rigid certainty |
+| Cancer | Capricorn | Emotional openness vs. control |
+| Leo | Aquarius | Self-expression vs. detachment |
+| Virgo | Pisces | Discernment vs. escapism |
+| Libra | Aries | Cooperation vs. self-assertion |
+| Scorpio | Taurus | Transformation vs. stagnation |
+| Sagittarius | Gemini | Faith vs. scattered thinking |
+| Capricorn | Cancer | Self-reliance vs. dependence |
+| Aquarius | Leo | Collective vs. personal drama |
+| Pisces | Virgo | Surrender vs. perfectionism |
+
+**Each pair**: North Node qualities to develop (+) / South Node tendencies to release (−)
+
+### Key Nodal Aspects
+
+- **Planets conjunct South Node**: Past-life energy, gifts and attachments you fall back into
+- **Planets conjunct North Node**: Present-life growth catalysts
+- **Planets square the nodes**: "Skipped steps" — unresolved issues that must be re-engaged
+- **Saturn conjunct North Node**: Karmic contract; relationship/lesson pushes growth
+- **Saturn square Nodes**: Karmic block; timing/life paths hard to synchronize
+- **Pluto conjunct South Node**: Intense past-life pattern involving power, control, trauma
+
+---
+
+## Saturn: Lord of Karma
+
+### Saturn's Karmic Meaning
+
+- **Karmic debts & unfinished business** — areas where you "owe work"
+- **Fears and inhibitions** — karmic memory of having failed or been wounded
+- **Discipline and mastery** — where the soul must mature through effort
+- **Potential for strength** — once integrated, becomes your most solid foundation
+
+### Saturn Interpretation Steps
+
+1. **Sign**: Style of karma and psychological coloration
+2. **House**: Life area where karma is most active
+3. **Aspects**: Specific karmic stories (hard aspects = tension points)
+4. **Condition**: Direct vs. retrograde (retrograde = unfinished duties)
+
+### Saturn Retrograde
+
+- Points to unfinished past-life duties, misused authority, avoided responsibility
+- Life tends to feel internally heavy: self-criticism, delays, sense of "being behind"
+- Framed as correction and maturation, not punishment
+- Once tasks are embraced, Saturn offers stability and inner peace
+
+### Key Saturn Aspects (Karmic Meaning)
+
+| Aspect | Past-Life Theme | Present-Life Lesson |
+|---|---|---|
+| Saturn–Sun | Misuse of authority or being controlled by it | Healthy ego, integrity |
+| Saturn–Moon | Family/emotional wounding, abandonment | Emotional maturity |
+| Saturn–Venus | Love/money exploited or withheld | Self-worth, boundaries |
+| Saturn–Mars | Violence, aggression, or cowardice | Disciplined action |
+| Saturn–Jupiter | Excess, dogmatism, rigid moral codes | Faith with realism |
+
+---
+
+## Pluto: Evolutionary Astrology Layer
+
+*From Jeffrey Wolf Green's Evolutionary Astrology*
+
+### Pluto = The Soul
+
+- Pluto by sign and house describes **core past-life desires** that shaped prior incarnations
+- Shows the soul's deepest unconscious security needs and complexes
+- Evolutionary astrology starts every interpretation from Pluto
+
+### The Two Trinities
+
+**Trinity of the Past:**
+1. Pluto — core past-life desires
+2. South Node — operational mode (how desires were expressed)
+3. Ruler of South Node — specific archetypal themes
+
+**Trinity of the Future:**
+1. Pluto's Polarity Point (PPP) — archetypes to embrace for evolution
+2. North Node — new operational mode to develop
+3. Ruler of North Node — arenas for evolutionary integration
+
+### Pluto's Polarity Point (PPP)
+
+- Simply the degree opposite Pluto
+- Shows what must be developed to **balance Pluto's existing center of gravity**
+- Engaging the PPP house/sign is how the soul evolves out of repetitive past-life patterns
+
+### Reading Sequence (Evolutionary Astrology)
+
+1. Pluto by sign and house
+2. South Node by sign and house
+3. Ruler of the South Node
+4. Pluto's polarity point
+5. North Node by sign and house
+6. Ruler of the North Node
+
+---
+
+## Retrograde Planets and Karma
+
+- Retrograde planets indicate **unfinished business from past lives**
+- They "break the time barrier," carrying karmic material into the present incarnation
+- Schulman describes **three vibrational modes** for retrograde planets
+- Prioritize retrograde **personal planets** (Mercury, Venus, Mars) in karmic reading
+- Retrograde **Saturn** and **Pluto** add significant karmic weight
+
+---
+
+## 12th House: Spiritual Karma
+
+- The 12th house holds **spiritual and collective karma**
+- Planets here indicate unconscious patterns and past-life memories
+- The 12th house ruler's sign and house show how spiritual lessons manifest
+- Strong 12th house emphasis can indicate past lives of isolation, monasticism, or hidden service
+
+---
+
+## Karmic Synastry: Relationship Karma
+
+When analyzing two charts for karmic connections:
+
+### Key Indicators
+
+- **One person's Saturn on the other's South Node**: Past-life bonds with themes of duty, control, or unfinished business
+- **One person's Pluto on the other's South Node**: Powerful past-life intensity (power, trauma, obsession)
+- **One person's planet conjunct the other's North Node**: Fated connection; the planet person helps the Node person grow
+- **One person's planet conjunct the other's South Node**: "I've known you forever" feeling; past-life familiarity
+- **Saturn square either person's nodes**: Karmic tension block; forces confrontation with unfinished lessons
+
+---
+
+## Transit Triggers for Karmic Periods
+
+| Transit | Karmic Meaning |
+|---|---|
+| **Saturn conjunct South Node** | Confronting past-life patterns; karmic reckoning |
+| **Saturn conjunct North Node** | Pushed toward growth; serious life-direction lessons |
+| **Pluto conjunct South Node** | Deep transformation of past-life patterns; can be slow and intense |
+| **Pluto conjunct North Node** | Transformative growth; power struggles around life direction |
+| **Node return (~18.6 years)** | Completion of a karmic cycle; major nodal activation |
+| **Eclipse conjunct natal nodes** | Fateful turning point; activation of nodal axis |
+| **Saturn return (~28-30)** | Karmic turning point; conscious engagement with nodal axis |
+
+---
+
+## Synthesizing a Karmic Reading
+
+1. Start with the **nodal axis** — what is the soul moving from and toward?
+2. Add **Saturn** — where must discipline and responsibility be applied?
+3. Add **Pluto** — what deep soul desires and transformations are at work?
+4. Check **retrogrades** — which functions carry unfinished business?
+5. Check **12th house** — what spiritual or hidden karma is present?
+6. Identify **repeated themes** across all indicators
+7. For relationships, overlay **karmic synastry** indicators
+8. Note **transit activations** — when karmic themes are triggered
+
+## Important Caveats
+
+- Karma explains the "why," not the "what will happen"
+- The chart shows **karmic conditions**, not fixed destiny
+- Free will operates in how one responds to these conditions
+- Difficult karmic indicators are **growth opportunities**, not punishments
+- Past-life conditioning shows up as **psychological patterns**, not necessarily literal events

+ 272 - 0
agent-guides/natal-astrology.md

@@ -0,0 +1,272 @@
+# Natal Astrology — Agent Interpretation Guide
+
+> For agents using the astro MCP to perform natal chart interpretation.
+
+## Framework
+
+A natal chart is a symbolic map of personality, potential, and life themes. The art of interpretation is **synthesis** — weaving individual data points (planets, signs, houses, aspects) into a coherent narrative about the person.
+
+### Core Principle
+
+> **Planets** = *what* is happening (psychological functions)
+> **Signs** = *how* the energy behaves (style, flavor)
+> **Houses** = *where* in life it plays out (life areas)
+> **Aspects** = *how the parts interact* (support, tension, fusion)
+
+---
+
+## Reading Order
+
+Use this checklist for every natal reading:
+
+### 1. Chart Overview
+- **Element balance**: Which elements dominate? Which are missing?
+- **Modality balance**: Cardinal/fixed/mutable distribution
+- **Hemisphere emphasis**: Upper/lower, east/west
+- **Chart shape**: Bundle, bowl, splash, etc.
+- **Stelliums**: Any sign or house with 3+ planets?
+
+### 2. The Big Three
+- **Sun** (sign + house): Core identity and purpose
+- **Moon** (sign + house): Emotional needs and instincts
+- **Ascendant** (sign): Persona and approach to life
+- **Chart ruler**: Ruler of the Ascendant — its sign, house, and aspects
+
+### 3. Angles and Their Rulers
+- Signs on the 1st, 4th, 7th, 10th house cusps
+- Rulers of these angles — their condition
+- Planets conjunct any angle (highly emphasized)
+
+### 4. Personal Planets
+- **Mercury**: Thinking and communication style
+- **Venus**: Love style, values, aesthetics
+- **Mars**: Drive, assertiveness, conflict style
+- For each: sign + house + key aspects
+
+### 5. Social and Outer Planets
+- **Jupiter**: Growth, opportunity, faith
+- **Saturn**: Challenges, lessons, mastery
+- **Uranus/Neptune/Pluto**: Generational themes; personal when tightly aspecting angles or personal planets
+
+### 6. Lunar Nodes
+- **South Node**: Past patterns, comfort zone, innate talents
+- **North Node**: Growth direction, life purpose
+- Aspects to the nodal axis
+
+### 7. Aspect Patterns
+- **T-squares**: Stress + potential (identify apex planet)
+- **Grand trines**: Natural talent and ease
+- **Grand crosses**: Intense tension requiring balance
+- **Yods**: Fated, sensitive points
+
+### 8. Synthesis
+- Identify 3–5 **recurring themes**
+- Translate technical factors into plain language
+- What does this person need to feel fulfilled?
+- Where are their strengths and growth edges?
+
+---
+
+## The Big Three in Detail
+
+### Sun — Core Identity
+
+The Sun represents your **essential self** — who you are when you're being most authentic.
+
+- **Sign**: How you shine. A Sun in Aries shines through initiative and courage. A Sun in Pisces shines through compassion and imagination.
+- **House**: Where you seek recognition and purpose. Sun in the 10th = purpose through career. Sun in the 4th = purpose through home and family.
+- **Key aspects**: Modify the expression. Sun conjunct Saturn = serious, disciplined identity. Sun square Uranus = rebellious, unpredictable identity.
+
+### Moon — Emotional World
+
+The Moon represents your **inner self** — emotional needs, instincts, what makes you feel safe.
+
+- **Sign**: Your emotional style. Moon in Cancer = needs nurturing and security. Moon in Aquarius = needs freedom and intellectual connection.
+- **House**: Life area that strongly affects mood. Moon in the 7th = emotions tied to relationships. Moon in the 2nd = emotions tied to financial security.
+- **Key aspects**: Moon-Venus = natural emotional warmth. Moon-Saturn = emotional restraint or insecurity. Moon-Mars = emotional reactivity.
+
+### Ascendant — Persona
+
+The Ascendant is the **mask** you wear — how others first experience you.
+
+- **Sign**: Your outward style. Aries Ascendant = direct, energetic first impression. Libra Ascendant = charming, diplomatic first impression.
+- **Chart ruler**: The planet ruling your Ascendant sign is the **most important planet** in the chart. Its condition (sign, house, aspects) tells you where your life energy is directed.
+
+---
+
+## Planet Interpretation Quick Reference
+
+### Personal Planets
+
+| Planet | Function | Sign Shows | House Shows |
+|---|---|---|---|
+| **Sun** | Identity, purpose, vitality | How you shine | Where you seek recognition |
+| **Moon** | Emotions, needs, instincts | Emotional style | What affects your mood |
+| **Mercury** | Thinking, communication | Mental style | Area of focused learning |
+| **Venus** | Love, values, pleasure | Love style | What you value/attract |
+| **Mars** | Drive, action, conflict | How you assert | Where you take action |
+
+### Social Planets
+
+| Planet | Function | Sign Shows | House Shows |
+|---|---|---|---|
+| **Jupiter** | Growth, faith, expansion | How you grow | Where opportunity flows |
+| **Saturn** | Discipline, fear, mastery | How you're tested | Where you must build |
+
+### Outer Planets
+
+| Planet | Function | Personal When... |
+|---|---|---|
+| **Uranus** | Freedom, disruption, innovation | Conjunct Sun/Moon/Ascendant or personal planet |
+| **Neptune** | Dreams, spirituality, dissolution | Conjunct Sun/Moon/Ascendant or personal planet |
+| **Pluto** | Power, transformation, depth | Conjunct Sun/Moon/Ascendant or personal planet |
+
+---
+
+## Sign Interpretation Quick Reference
+
+### By Element
+
+| Element | Expression |
+|---|---|
+| **Fire** | Inspiring, energetic, action-oriented, passionate |
+| **Earth** | Practical, grounded, material, steady |
+| **Air** | Intellectual, communicative, social, conceptual |
+| **Water** | Emotional, intuitive, empathic, deep |
+
+### By Modality
+
+| Modality | Expression |
+|---|---|
+| **Cardinal** | Initiating, leading, action-oriented |
+| **Fixed** | Determined, persistent, resistant to change |
+| **Mutable** | Adaptable, flexible, versatile |
+
+---
+
+## House Interpretation Quick Reference
+
+### Angular Houses (1, 4, 7, 10) — Most Powerful
+
+| House | Domain | "I am..." |
+|---|---|---|
+| 1st | Self, identity, appearance | "I am..." |
+| 4th | Home, family, roots | "I come from..." |
+| 7th | Partnerships, relationships | "I relate through..." |
+| 10th | Career, public role | "I achieve..." |
+
+### Succedent Houses (2, 5, 8, 11) — Stabilizing
+
+| House | Domain | Focus |
+|---|---|---|
+| 2nd | Values, money, self-worth | Security and resources |
+| 5th | Creativity, romance, children | Self-expression and joy |
+| 8th | Shared resources, intimacy, transformation | Depth and merging |
+| 11th | Friends, groups, goals | Community and aspirations |
+
+### Cadent Houses (3, 6, 9, 12) — Transitional
+
+| House | Domain | Focus |
+|---|---|---|
+| 3rd | Communication, learning, siblings | Information and connection |
+| 6th | Work, health, routines | Service and daily life |
+| 9th | Philosophy, travel, higher mind | Meaning and expansion |
+| 12th | Subconscious, spirituality, retreat | Inner world and surrender |
+
+---
+
+## Aspect Interpretation Quick Reference
+
+### Major Aspects
+
+| Aspect | Angle | Meaning | Keyword |
+|---|---|---|---|
+| **Conjunction** | 0° | Fusion, intensification | Blending |
+| **Sextile** | 60° | Cooperation, opportunity | Supporting |
+| **Square** | 90° | Friction, tension, growth | Challenging |
+| **Trine** | 120° | Harmony, ease, talent | Flowing |
+| **Opposition** | 180° | Polarity, projection, balance | Tension |
+
+### How to Interpret Any Aspect
+
+1. **Identify the two planets** — what functions do they represent?
+2. **Note the aspect type** — supportive or challenging?
+3. **Check the houses** — which life areas are involved?
+4. **Check the signs** — what's the tone of the interaction?
+5. **Synthesize**: "The [Planet A] function and [Planet B] function are [blended/in tension/in harmony], creating [dynamic] between [life area A] and [life area B]."
+
+### Key Aspect Patterns
+
+| Pattern | Shape | Meaning |
+|---|---|---|
+| **T-square** | 2 oppositions + 1 square | Stress + potential; apex planet is focal point |
+| **Grand trine** | 3 trines (same element) | Natural talent and ease; can be too comfortable |
+| **Grand cross** | 2 oppositions + 4 squares | Intense tension; requires balance and flexibility |
+| **Yod** | 2 sextiles + 2 quincunxes | Fated, sensitive point; apex planet is destiny focal point |
+| **Stellium** | 3+ planets in sign/house | Intense focus on that sign/house theme |
+
+---
+
+## Synthesis: Building the Narrative
+
+### What to Look For
+
+1. **Repeated themes**: If Sun, Moon, and Ascendant all emphasize relationships → relationship is a core life theme
+2. **Contradictions**: Sun in Aries (assertive) but Moon in Libra (accommodating) → inner tension between self-assertion and harmony
+3. **Compensations**: Missing fire element but Mars in the 1st house → fire expressed through action rather than identity
+4. **Dominant aspects**: Multiple squares to Saturn → a "Saturnian" life with heavy lessons around responsibility
+5. **Unaspected planets**: Function operates independently, sometimes unconsciously
+
+### Output Structure for a Natal Reading
+
+1. **Overview**: Core personality in 2–3 sentences
+2. **The Big Three**: Sun, Moon, Ascendant — identity, emotions, persona
+3. **Chart Ruler**: Where life energy is directed
+4. **Key Planet Placements**: 3–5 most significant planet-in-sign-in-house combinations
+5. **Major Aspects**: 3–5 tightest/most significant aspects with interpretation
+6. **Aspect Patterns**: Any T-squares, grand trines, grand crosses
+7. **Life Themes**: Recurring patterns across the chart
+8. **Strengths and Gifts**: Natural talents and supportive aspects
+9. **Growth Edges**: Challenging aspects and what they're teaching
+10. **Current Transits** (optional): What's activating the chart right now
+
+### Interpretation Tone
+
+- **Empowering, not deterministic**: The chart shows potential and tendencies, not fixed fate
+- **Strengths-first**: Lead with gifts before discussing challenges
+- **Actionable**: For each challenge, suggest what the person can *do* with it
+- **Plain language**: Avoid jargon when possible; translate technical terms
+- **Holistic**: Weave individual placements into a coherent story, not a list of bullet points
+
+---
+
+## Quick Interpretation Templates
+
+### Planet in Sign in House
+
+> **[Planet]** in **[Sign]** in **[House]** → [Planet function] expressed in the style of [Sign keywords] through the life area of [House].
+
+**Example**: "Mars in Capricorn in the 6th house → Drive and initiative (Mars) expressed with discipline and ambition (Capricorn) through daily work and health routines (6th)."
+
+### Aspect
+
+> **[Planet A]** in **[Sign X]** **[aspect]** **[Planet B]** in **[Sign Y]** → The [Planet A function] and [Planet B function] are [blended/in tension/in harmony], creating [dynamic] between [life area A] and [life area B].
+
+**Example**: "Venus in Taurus trine Saturn in Virgo → Love and affection (Venus) flow naturally with discipline and commitment (Saturn), creating a stable, enduring approach to relationships and values."
+
+### Chart Ruler
+
+> The chart ruler is **[Planet]** in **[Sign]** in **[House]** → Life energy is strongly directed toward [house themes], expressed through [sign style].
+
+**Example**: "The chart ruler is Mercury in Sagittarius in the 9th house → Life energy is directed toward higher learning, philosophy, and travel, expressed through an optimistic, exploratory communication style."
+
+---
+
+## Common Pitfalls
+
+1. **Sun sign only**: The Sun is important, but it's only one piece. Always read the full chart.
+2. **Overemphasizing hard aspects**: Squares and oppositions are growth opportunities, not curses.
+3. **Ignoring the chart ruler**: The ruler of the Ascendant is often the most important planet in the chart.
+4. **Reading planets in isolation**: Always consider sign + house + aspects together.
+5. **Deterministic language**: Avoid "you will" — use "you tend to," "you may find," "there's a theme of."
+6. **Missing the forest for the trees**: Step back and identify the 3–5 big themes before diving into details.

+ 250 - 0
agent-guides/relationship-astrology.md

@@ -0,0 +1,250 @@
+# Relationship Astrology — Agent Interpretation Guide
+
+> For agents using the astro MCP to perform relationship chart interpretation.
+
+## Overview
+
+Relationship astrology uses four main techniques to analyze the dynamics between two people:
+
+| Technique | What It Shows |
+|---|---|
+| **Synastry** | Interaction patterns — how Person A's planets trigger Person B's planets |
+| **Composite Chart** | The relationship as its own entity — structure, identity, public face |
+| **Davison Chart** | The relationship's "birth" — inner experience, emotional tone, long-term evolution |
+| **Coalescent Chart** | Harmonic responsiveness to transits (rarely used) |
+
+**For serious analysis, use all three main techniques plus natal charts.** They are complementary layers, not competing methods.
+
+---
+
+## Reading Workflow
+
+### Step 1: Natal Charts
+Understand each person individually first. What are their core needs, attachment styles, relationship patterns? This provides context for everything that follows.
+
+### Step 2: Synastry
+Analyze the **interaction patterns** between the two charts:
+- Tightest interchart aspects (tightest orbs = most significant)
+- Venus-Mars contacts (chemistry)
+- Moon contacts (emotional bonding)
+- Saturn contacts (commitment, karma, heaviness)
+- Node contacts (fated connections)
+- House overlays (which life areas are activated)
+
+### Step 3: Composite Chart
+Analyze the **relationship as its own entity**:
+- Composite Sun (identity and purpose)
+- Composite Moon (emotional tone)
+- Angular planets (loudest voices)
+- Key aspect patterns (especially Saturn, Pluto, Neptune)
+- House emphasis (life area of focus)
+
+### Step 4: Davison Chart
+Analyze the **relationship's inner experience**:
+- Same approach as composite but focused on emotional/internal tone
+- More accurate for timing techniques (progressions, solar arcs)
+- Use for long-term evolution tracking
+
+### Step 5: Synthesis
+Find **repeated themes** across synastry + composite + Davison. These recurring patterns are the core narrative of the relationship.
+
+---
+
+## Synastry Interpretation
+
+### Key Principle
+Each person **plays the role** of their planet in an interchart aspect. If A's Saturn conjuncts B's Sun, A takes on the Saturn role when B expresses Sun qualities. Think of it as a dance where each person activates different roles depending on context.
+
+### Interchart Aspects: Quick Reference
+
+#### Venus-Mars (Chemistry)
+| Aspect | Meaning |
+|---|---|
+| Conjunction | Intense, obvious physical and romantic attraction |
+| Trine/Sextile | Desire and affection flow easily; compatible sexual needs |
+| Opposition | Strong magnetism with push-pull dynamics |
+| Square | Highly stimulating but prone to friction and fights |
+
+#### Moon-Venus (Emotional Warmth)
+- Creates tenderness, caring, feeling emotionally "fed" by partner
+- One of the best aspects for domestic comfort and feeling cherished
+- Conjunction, trine, sextile = easy flow; hard aspects = needs clash
+
+#### Sun-Moon (Core Compatibility)
+- Deep understanding at ego and emotional levels
+- Conjunction/trine/sextile: natural "getting" of each other
+- Opposition: complementary but different needs
+- Square: intense attraction with emotional friction
+
+#### Saturn Aspects (Commitment and Karma)
+- Conjunctions feel fated, dutious, sometimes heavy
+- Trines/sextiles provide stability and reliability
+- Squares/oppositions bring tests, delays, pressure
+- Not inherently bad — many long marriages have strong Saturn contacts
+
+#### Node Contacts (Fated Connections)
+- Planets on North Node: destined connection, growth catalyst
+- Planets on South Node: "known you forever" past-life feel
+- Saturn on North Node: karmic teacher relationship
+- Saturn square Nodes: karmic block, timing challenges
+
+### House Overlays
+When Planet A falls in Planet B's house:
+- **1st house**: Strongly visible, shapes self-image
+- **4th house**: Emotional significance, feeling "at home"
+- **7th house**: Partnership significance, embodies relating energy
+- **8th house**: Intense psychological/sexual bond
+- **10th house**: Public significance, affects career
+- **12th house**: Hidden or spiritual connection, can be confusing
+
+### Orb Guidelines
+- Personal planets (Sun through Saturn): 5° max
+- Outer planets: 3° max
+- Tighter orbs = stronger effect
+- Using significance scoring: conjunction/opposition > square > trine > sextile
+
+---
+
+## Composite Chart Interpretation
+
+### Key Principle
+**The composite chart describes the relationship entity, not either person.** Composite Moon in Scorpio doesn't mean either person is emotionally intense — it means the *relationship itself* has that quality.
+
+### Reading Order
+1. **Chart shape**: House clusters (upper/lower, east/west hemispheres)
+2. **Composite Ascendant**: How the relationship presents itself
+3. **Composite Sun**: Relationship identity and purpose
+4. **Composite Moon**: Emotional tone
+5. **Angular planets**: Loudest voices
+6. **Most-aspected planet**: Recurring theme
+7. **Unaspected planets**: Blind spots
+8. **Chart ruler**: How the relationship navigates challenges
+
+### Composite Planet Meanings
+| Planet | Meaning in Composite |
+|---|---|
+| Sun | Core identity and purpose of the relationship |
+| Moon | Emotional tone, comfort, daily rhythms |
+| Mercury | Communication style as a couple |
+| Venus | Love style, affection, shared values |
+| Mars | Drive, passion, conflict style |
+| Jupiter | Growth, optimism, shared beliefs |
+| Saturn | Structure, commitment, tests |
+| Uranus | Unpredictability, freedom, innovation |
+| Neptune | Idealism, spiritual connection, illusion |
+| Pluto | Power dynamics, transformation, depth |
+
+### Composite Aspects
+- **Orbs**: 3° max (tighter than natal). Sub-1° = defining feature.
+- **Saturn aspects**: Structural foundation. No Saturn = free but unstable. Strong Saturn = enduring but demanding.
+- **Pluto aspects**: Power struggles and transformation potential.
+- **Neptune aspects**: Idealization risk. Watch for disillusionment when glamour fades.
+- **Sun-Saturn conjunction**: Core defining feature — serious but potentially very long-lasting.
+
+### Common Mistakes
+1. Reading composite as a natal chart for one person
+2. Treating difficult aspects as relationship doom
+3. Reading composite in isolation (always use with synastry + natal)
+
+---
+
+## Davison Chart Interpretation
+
+### Technical Note
+The Davison chart is a **real chart at a real moment in time** (midpoint of two births), unlike the composite which is purely symbolic. This means it can be progressed and directed like a natal chart.
+
+### Composite vs. Davison
+| | Composite | Davison |
+|---|---|---|
+| Nature | Symbolic blueprint | Real moment in space/time |
+| Shows | Structure, public face | Inner tone, emotional experience |
+| Best for | How relationship functions | How it feels and evolves |
+| Timing | Transits | Transits + progressions + solar arcs |
+
+### Practical Staging
+- **Synastry** → when people first meet
+- **Composite** → once relationship forms
+- **Davison** → once formally committed
+
+### When They Differ
+Look for **repeated themes** across both. They are "two versions of the same story." Same planets emphasized, similar house themes = core relationship narrative confirmed.
+
+---
+
+## Relationship Type Indicators
+
+### Romantic / Sexual
+- Venus-Mars aspects (synastry + composite)
+- 5th and 8th house emphasis
+- Moon contacts for emotional bonding
+- Saturn contacts for commitment
+- Pluto contacts for intensity
+
+### Business / Professional
+- Mercury contacts for communication
+- 6th and 10th house emphasis
+- Saturn contacts for structure
+- Jupiter contacts for growth
+- Uranus contacts for innovation
+
+### Family Dynamics
+- Moon contacts for emotional patterns
+- 4th house emphasis
+- Saturn contacts for duty/parental roles
+- Node contacts for karmic family patterns
+
+### Friendships
+- Mercury contacts for intellectual connection
+- 11th house emphasis
+- Jupiter contacts for shared optimism
+- Uranus contacts for unconventional bonds
+
+---
+
+## Timing Relationship Events
+
+### Composite/Davison Transits
+- **Saturn transits** to composite angles: Tests and restructuring
+- **Jupiter transits** to composite angles: Expansion and growth
+- **Uranus transits**: Sudden changes, disruption
+- **Pluto transits**: Deep transformation
+- **Eclipses** hitting composite angles: Fateful turning points
+
+### Transit Preview
+Use the astro MCP's transit preview capability on the composite and Davison charts to identify windows of activation.
+
+---
+
+## Karmic Relationship Indicators
+
+(See karmic-agent-GUIDE.md for full detail)
+
+### Key Indicators Across All Three Layers
+- Saturn-Node contacts between charts (karmic contracts)
+- Pluto-South Node contacts (intense past-life patterns)
+- Composite/Davison chart emphasizing 8th house (shared transformation)
+- South Node contacts suggesting familiarity from past lives
+- Saturn-heavy composites suggesting serious, destiny-laden relationships
+
+---
+
+## Synthesis: Building the Narrative
+
+When synthesizing a relationship reading across all layers:
+
+1. **Identify the core narrative** — What story do all three layers tell together?
+2. **Note contradictions** — Where do the layers diverge? These are areas of complexity.
+3. **Assess karmic weight** — How many karmic indicators are present? (Saturn, Pluto, Nodes)
+4. **Determine relationship type** — Romantic, karmic, business, casual, fated?
+5. **Identify growth edges** — What are the challenging aspects teaching?
+6. **Check timing** — What transits are activating the relationship now or soon?
+
+### Output Structure for Agents
+A good relationship analysis output should include:
+- **Overview**: Core narrative in 2-3 sentences
+- **Synastry highlights**: Top 5-7 interchart aspects with interpretation
+- **Composite reading**: Relationship identity, strengths, challenges
+- **Davison reading** (if calculated): Inner emotional experience
+- **Karmic indicators**: Any significant past-life or soul-contract themes
+- **Current transit influences**: What's activating the relationship right now
+- **Guidance**: What each person can do to support the relationship's growth

+ 494 - 0
karmic-astrology.md

@@ -0,0 +1,494 @@
+# Karmic Astrology — A Research Compiled Reference
+
+> Compiled from multiple online sources, June 2026.
+> Primary sources: Martin Schulman's *Karmic Astrology* series, Jan Spiller's *Astrology for the Soul*, Jeffrey Wolf Green's Evolutionary Astrology, WildWitchWest, Almanac.com, and various astrology educators.
+
+---
+
+## Table of Contents
+
+1. [What Is Karmic Astrology?](#what-is-karmic-astrology)
+2. [The Lunar Nodes — Core of Karmic Interpretation](#the-lunar-nodes--core-of-karmic-interpretation)
+   - South Node: Past Life Patterns
+   - North Node: Evolutionary Direction
+   - The Axis of Evolution
+3. [Node Sign Interpretations (Schulman / Spiller)](#node-sign-interpretations)
+   - Aries ⇄ Libra
+   - Taurus ⇄ Scorpio
+   - Gemini ⇄ Sagittarius
+   - Cancer ⇄ Capricorn
+   - Leo ⇄ Aquarius
+   - Virgo ⇄ Pisces
+4. [Node House Placements](#node-house-placements)
+5. [Saturn — Lord of Karma](#saturn--lord-of-karma)
+6. [Pluto in Evolutionary Astrology (Jeffrey Wolf Green)](#pluto-in-evolutionary-astrology)
+   - The Two Trinities
+   - Pluto's Polarity Point
+7. [Retrograde Planets and Karma (Schulman Vol. II)](#retrograde-planets-and-karma)
+8. [The Part of Fortune (Schulman Vol. III)](#the-part-of-fortune)
+9. [Other Karmic Indicators in the Chart](#other-karmic-indicators)
+10. [The Four Volumes of Martin Schulman's Karmic Astrology](#the-four-volumes-of-martin-schulkarmic-astrology)
+11. [Key Authors and Resources](#key-authors-and-resources)
+12. [Sources and Further Reading](#sources-and-further-reading)
+
+---
+
+## What Is Karmic Astrology?
+
+Karmic astrology is a branch of astrology that interprets the natal chart through the lens of **reincarnation and karma**. Its core premise:
+
+- **Present-life circumstances are the result of causes set in prior incarnations.** Karma is not simply "punishment," but an **educational process** through which the soul balances past excesses and distortions.
+- **The birth chart is a karmic blueprint** — it shows *what* was carried over from other lives (habits, fears, talents) and *where* growth is now required.
+- **Astrology as a map of karmic patterns**: the chart shows potential, not fixed destiny. Free will operates in how one responds to karmic conditions.
+- **Cause and effect across lifetimes** ("law of karma"): events and relationships reflect deeper lessons rather than random fate.
+
+> "The charts tell us potential, not necessarily future." — WildWitchWest
+
+---
+
+## The Lunar Nodes — Core of Karmic Interpretation
+
+The **Moon's Nodes** are the single most important factor in karmic astrology. They are mathematical points where the Moon's orbit intersects the Earth's ecliptic (the path around the Sun). In Vedic astrology they are called **Rahu** (North Node) and **Ketu** (South Node) — the dragon's head and tail.
+
+The nodes complete a full circle through the 12 zodiac signs every **18 years**, which is why people born within a few years of each other share the same nodal placement.
+
+### South Node: Past Life Patterns
+
+The **South Node** represents:
+- **Ingrained tendencies** from previous incarnations
+- **Overdeveloped or misused** attitudes, emotional reflexes, and lifestyles
+- **The comfort zone** — what feels natural but can hold you back
+- **Innate talents and skills** brought in from past lives (the "easy mode")
+- **Emotional patterns, habits, and relational dynamics** that feel familiar but limiting
+- Martin Schulman explicitly describes the South Node as the **"Achilles' heel"** of the chart
+
+> "The South Node is not 'bad' — it shows skills and instincts you already have, but you can lean on them too much and stall growth." — WildWitchWest
+
+### North Node: Evolutionary Direction
+
+The **North Node** represents:
+- The **qualities and experiences the soul is now trying to develop**
+- The **evolutionary path** — unfamiliar, sometimes uncomfortable
+- **New responses** required rather than repetition of the past
+- The **soul's "to-do list"** for this lifetime (Jan Spiller)
+- Direction that feels less natural but is essential for growth
+
+### The Axis of Evolution
+
+Schulman treats the nodes as an **axis of evolution**:
+- The soul is in a **transition from South Node patterns to North Node development**
+- Not "abandoning" one sign for the other, but **re-balancing both**
+- Planets conjunct the North Node help point toward growth
+- Planets conjunct the South Node show the past-life energy you instinctively fall back into
+- **Squares to the nodes** are called "skipped steps" — unresolved issues that must be re-engaged
+
+---
+
+## Node Sign Interpretations
+
+Each North/South Node pair implies a characteristic karmic story. Below is a synthesis from **Martin Schulman**, **Jan Spiller**, and multiple karmic astrology sources.
+
+### North Node Aries / South Node Libra (1st/7th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Libra)** | Focused on keeping the peace, being a supporting player, diplomat, responsible for harmony. Put others' desires on hold, avoided arguments, accepted poor behavior to maintain comfort. |
+| **This life (North Node Aries)** | Must develop **independence, courage, assertiveness, self-trust**. Learn to stand up for yourself, express needs, create and enforce boundaries. Being selfish vs. being selfless — finding the balance. |
+| **Shadow to release** | Selflessness to the point of self-erasure, co-dependence, attachment to fairness/justice as a tit-for-tat mentality |
+| **Gifts brought in** | Natural diplomacy, awareness of others, ability to cooperate |
+
+### North Node Taurus / South Node Scorpio (2nd/8th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Scorpio)** | Spent much time managing crises. Excellent at handling people at their worst. May have experienced betrayal, leading to trust issues and strong BS detector. Control and emotional intensity. |
+| **This life (North Node Taurus)** | Must build **stability, self-worth, patience, gratitude**. Focus on security — developing self-worth rather than managing everyone else's problems. Learn to let go of control and trust the universe. |
+| **Shadow to release** | Impatience, attraction to crisis, inappropriate intensity, obsessive-compulsive tendencies, jealousy, need for revenge |
+| **Gifts brought in** | Emotional depth, crisis management skills, ability to handle intensity |
+
+### North Node Gemini / South Node Sagittarius (3rd/9th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Sagittarius)** | Spent lives in academia, spiritual retreats, or wandering in search of truth. Need for honesty permeates. Rigid about ethics/morals. Self-righteous or hypocritical tendencies. |
+| **This life (North Node Gemini)** | Must **open mind to new ideas, listen without plotting response, let go of the need to be 'right'**. Lighten up! Maintain curiosity through continuous learning. Not everyone operates with the same rigorous ethics — learn to live and let live. |
+| **Shadow to release** | Self-righteousness, aloofness, needing to be right, careless spontaneity, judging situations based on past experience, taking shortcuts |
+| **Gifts brought in** | Big-picture thinking, philosophical depth, love of truth and learning |
+
+### North Node Cancer / South Node Capricorn (4th/10th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Capricorn)** | Held positions of authority, heavy responsibilities, ambition and control were focal points. May have become workaholic to avoid intimacy. Sometimes used status as a way to avoid vulnerability. |
+| **This life (North Node Cancer)** | Must **validate feelings, nurture self and others, create a safe environment for emotional expression**. Build honest disclosure of feelings and insecurities. Learn to let go of the need for control (often based on insecurity). |
+| **Shadow to release** | Needing to control everything, compulsion to take charge, ignoring process for goals, hiding feelings to gain respect |
+| **Gifts brought in** | Natural authority, competence, ability to take responsibility, structural thinking |
+
+### North Node Leo / South Node Aquarius (5th/11th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Aquarius)** | Spent lives trying to fit in, bucking the system, revolutionary or nonconformist ideals. Being an "alien." Sometimes spent lives in the wings as an understudy. |
+| **This life (North Node Leo)** | Must **take center stage, develop confidence, risk being seen**. Stand as an individual. Work on self-esteem — no more worrying about what others think! Visibility is part of the life lesson. |
+| **Shadow to release** | Detaching from emotional situations, aloofness, excessive daydreaming, running from confrontation, waiting for others to prompt action |
+| **Gifts brought in** | Innovative thinking, group awareness, ability to see the bigger picture |
+
+### North Node Virgo / South Node Pisces (6th/12th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Pisces)** | Spent lives as martyrs or victims. Felt helpless — lack of boundaries, escapist tendencies, tendency to withdraw or search for savior. Harsh criticism in past lives may have created perfectionism. |
+| **This life (North Node Virgo)** | Must develop **routine, order, service, discernment**. Bring compassion into the here and now. Assert strong but kind boundaries. Learn to go with the flow spiritually while being practical day-to-day. Excellent placement for healing professions. |
+| **Shadow to release** | Victimhood, confusion, escapism addictions, oversensitivity, self-doubt, over-analysis, obsessive worry, critical first reactions, perfectionism |
+| **Gifts brought in** | Natural compassion, spiritual sensitivity, healing abilities |
+
+### North Node Libra / South Node Aries (7th/1st House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Aries)** | Spent lifetimes being selfish or self-sufficient. Rugged individuals blazing own trails. Preput needs first. Became independent. |
+| **This life (North Node Libra)** | Must learn **cooperation, compromise, diplomacy, empathy**. Put others first (balanced). Find the "we" instead of just "me." Selflessness balanced with self-respect. |
+| **Shadow to release** | Impulsiveness, thoughtless self-assertion, selfishness, indifference to how one is seen |
+| **Gifts brought in** | Independence, courage, leadership ability |
+
+### North Node Scorpio / South Node Taurus (8th/2nd House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Taurus)** | Swathed in comfort. Chased pleasure and security, accumulated material goods. Kept safe — but now it feels like a trap. |
+| **This life (North Node Scorpio)** | Must **release possessions and the need to "own," develop trust**. Drop the shell — intimacy may be struggle but profound connections await. Shadow work is part of the journey. |
+| **Shadow to release** | Attachment to comfort, possessiveness, over-accumulation, stubbornness, resistance to change |
+| **Gifts brought in** | Self-worth, patience, loyalty, grounding energy |
+
+### North Node Sagittarius / South Node Gemini (9th/3rd House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Gemini)** | Collected information — perpetual student, teacher, writer. Intense curiosity, may have led to gossip, scattering energy, second-guessing. |
+| **This life (North Node Sagittarius)** | Must **trust hunches even without the backstory**. Develop faith. Communicate transparently — tell it like it is. Listen as well as you speak. Avoid pointless conflicts that scatter your forces. |
+| **Shadow to release** | Indecisiveness, seeking more information, saying what others want to hear, gossip, impatience for answers |
+| **Gifts brought in** | Curiosity, communication skills, love of learning |
+
+### North Node Capricorn / South Node Cancer (10th/4th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Cancer)** | Ongoing karmic cycles with family members. Themes of dependency, caregiving, or control played out repeatedly. May have defined self through family role. |
+| **This life (North Node Capricorn)** | Must **let go of the past, develop self-reliance**. Take responsibility for your own life without depending on family support/nurturing. Self-care plus goal orientation. Assert independence even if it makes loved ones uncomfortable. |
+| **Shadow to release** | Dependence, moodiness, insecurity leading to inaction, using past to avoid present, isolation |
+| **Gifts brought in** | Nurturing ability, emotional depth, family orientation |
+
+### North Node Aquarius / South Node Leo (11th/5th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Leo)** | May have been a royal or celebrity — felt special, entitled to the goodies. Drama when expectations unmet. |
+| **This life (North Node Aquarius)** | Must **practice humility, support and encourage others, share the spotlight**. Put the collective first. Lead from the heart — could change the world. Develops awareness of equality and group needs. |
+| **Shadow to release** | Insistence on getting one's way, melodramatic tendencies, attachment to need for approval, entitlement |
+| **Gifts brought in** | Creativity, confidence, leadership, warmth |
+
+### North Node Pisces / South Node Virgo (12th/6th House axis)
+
+| | Theme |
+|---|---|
+| **Past life (South Node Virgo)** | Spent in service — doctor, nurse, healer, teacher, servant. Tendency to fix everything and everyone. Perfectionism creates worry and anxiety. |
+| **This life (North Node Pisces)** | Must **let go of perfectionism**. Trust that you're doing your best — and so are others. Spiritual practices of great benefit. Connect with higher power. Practice compassion, empathy, gentleness toward self and others. |
+| **Shadow to release** | Over-analysis, obsessive worry, critical first reactions, fault-finding, perfectionism, rigid thinking |
+| **Gifts brought in** | Service orientation, healing skills, analytical ability, attention to detail |
+
+### Summary Table: Node Sign Polarities
+
+| North Node | South Node | Core Lesson |
+|---|---|---|
+| **Aries** | **Libra** | Develop independence, courage, self-trust; release overdependence on approval |
+| **Taurus** | **Scorpio** | Build stability, self-worth, patience; move away from crisis-seeking and control |
+| **Gemini** | **Sagittarius** | Cultivate curiosity, listening, flexibility; soften rigid certainty |
+| **Cancer** | **Capricorn** | Learn emotional openness, nurturing; loosen excessive control |
+| **Leo** | **Aquarius** | Embrace creativity, visibility, heart-led expression; reduce detachment |
+| **Virgo** | **Pisces** | Develop discernment, service, grounded routines; move away from escapism |
+| **Libra** | **Aries** | Learn cooperation, diplomacy; reduce impulsiveness and self-centeredness |
+| **Scorpio** | **Taurus** | Deepen trust, resilience, emotional transformation; release stagnation |
+| **Sagittarius** | **Gemini** | Grow through meaning, perspective, faith; temper scattered thinking |
+| **Capricorn** | **Cancer** | Strengthen responsibility and long-term structure; soften emotional defensiveness |
+| **Aquarius** | **Leo** | Balance individuality with contribution to collective; reduce need for personal drama |
+| **Pisces** | **Virgo** | Expand compassion, surrender, spiritual trust; release overanalysis and criticism |
+
+---
+
+## Node House Placements
+
+While signs define *how* the karmic drama plays out, **houses** determine *where* in life it plays out.
+
+| House | Life Area |
+|---|---|
+| 1st | Identity, self-image, independence |
+| 2nd | Money, security, values, self-worth |
+| 3rd | Learning, communication, siblings, local life |
+| 4th | Home, family, roots, emotional foundation |
+| 5th | Creativity, romance, children, self-expression |
+| 6th | Work, routines, health, daily service |
+| 7th | Relationships, partnership, cooperation |
+| 8th | Shared resources, intimacy, transformation, shared psychology |
+| 9th | Higher learning, travel, belief systems, philosophy |
+| 10th | Career, public role, reputation, authority |
+| 11th | Friends, groups, social goals, community |
+| 12th | Solitude, spirituality, unconscious patterns, surrender |
+
+A nodal reading combines the sign lesson with the house domain. For example: **North Node in Leo in the 10th house** means growth through creative self-expression in the career/public sphere. **South Node in Aquarius in the 4th** indicates a comfort zone of emotional detachment and group-identification in home/family matters that needs to be moved beyond.
+
+> "The North Node doesn't tell your life purpose — it gives you an access point for getting back on track when you've strayed from your soul's calling." — WildWitchWest
+
+### Saturn Return as Karmic Turning Point
+
+The South Node placement is usually **unconsciously identified with during the first 30 years** of life, up until the **Saturn Return** (~28-30), which often serves as a karmic turning point. This is when a person begins to consciously engage with their nodal axis and the deeper lessons it represents.
+
+---
+
+## Saturn — Lord of Karma
+
+### Why Saturn Is Called "Lord of Karma"
+
+Saturn is the **karmic taskmaster** — it rules time, consequences, and cause-and-effect. In both Vedic (Shani) and Western karmic astrology, Saturn holds the **"karmic key"** in the birth chart.
+
+### General Karmic Meaning of Saturn
+
+- **Karmic debts & unfinished business** — areas where you "owe work" due to avoidance, misuse of power, or irresponsibility in past lives
+- **Areas of fear and inhibition** — karmic memory of having failed, been punished, or been wounded
+- **Life lessons of discipline and mastery** — where the soul must mature through sustained effort
+- **Potential for great strength** — once lessons are accepted, the same area becomes your most solid foundation
+
+Saturn's energy asks us to go within, slow down, meditate, listen, and witness reality as it is. Lessons are framed as **learning, not retribution**. When you meet the challenge with humility and persistence, Saturn rewards like no other planet.
+
+### Saturn by House — Life Area of Karmic Focus
+
+| House | Karmic Theme |
+|---|---|
+| **1st** | Prior lives of identity struggle, avoidance of responsibility, misuse of personal power. Now: learn self-discipline, self-respect |
+| **2nd** | Money/self-worth karma. Past: greed or laziness. Now: material responsibility, realism, integrity |
+| **4th** | Family/mother/home karma. Past: emotional coldness, abandonment. Now: emotional maturity, healthy boundaries |
+| **5th** | Children/creativity/pleasure karma. Past: misuse of creative power or avoidance of joy. Now: disciplined creativity |
+| **7th** | Relationship karma. Past: broken promises, control, abandonment. Now: commitment, fairness, boundaries in partnership |
+| **8th** | Intimacy and shared resource karma. Past: psychological or occult misuse. Now: emotional honesty in intimacy |
+| **10th** | Career/status karma. Past: misuse of authority or dodging public responsibility. Now: earn your position through ethical leadership |
+| **11th** | Friends, groups, social ideals karma. Past: social abuse or detachment. Now: responsible community participation |
+| **12th** | Deep spiritual/collective karma. Past: isolation, imprisonment, or monastic life. Now: healthy solitude, spiritual practice, surrender |
+
+### Saturn by Sign — Style of Karmic Lessons
+
+- **Fire signs (Aries, Leo, Sagittarius)**: Lessons around courage, ego, leadership; past misuse: aggression, arrogance, dogmatism
+- **Earth signs (Taurus, Virgo, Capricorn)**: Lessons in material responsibility, work, realism; past karma: greed, laziness, rigid control
+- **Air signs (Gemini, Libra, Aquarius)**: Lessons in communication, fairness, group responsibility; past karma: lies, unfair deals, detachment
+- **Water signs (Cancer, Scorpio, Pisces)**: Emotional/spiritual karmic themes; past karma: caretaking to the point of martyrdom, emotional control, escapism
+
+### Key Saturn Aspects in Karmic Interpretation
+
+| Aspect | Past-Life Theme | Present-Life Lesson |
+|---|---|---|
+| **Saturn–Sun** | Misuse of authority, pride, harshly controlled by authority | Healthy ego, integrity, leadership without tyranny |
+| **Saturn–Moon** | Family/emotional wounding, abandonment, emotional coldness | Work through insecurity, learn to self-soothe and nurture others |
+| **Saturn–Venus** | Love/money/pleasure exploited or withheld | Self-worth, boundaries, authentic intimacy |
+| **Saturn–Mars** | Violence, aggression, or cowardice | Disciplined action, courage with conscience |
+| **Saturn–Jupiter** | Excess, dogmatism, rigid moral codes | Integrate faith with realism |
+
+### Saturn Retrograde
+
+Saturn retrograde in the natal chart is significant in karmic astrology:
+
+- Points to **unfinished past-life duties, misused authority, or avoided responsibility**
+- The individual may have "taken the easy route" or postponed crucial obligations in previous incarnations
+- Life tends to feel internally heavy: strong self-criticism, sense of "being behind," repeated delays
+- Framed as **correction and maturation**, not punishment
+- Once the person stops avoiding their tasks, Saturn offers stability, respect, and inner peace
+
+---
+
+## Pluto in Evolutionary Astrology
+
+*Key source: Jeffrey Wolf Green's Evolutionary Astrology*
+
+Jeffrey Wolf Green's system (**Evolutionary Astrology**) places Pluto at the very center of the chart as the core indicator of the soul's evolutionary journey.
+
+### Core Paradigm: Pluto = The Soul
+
+- **Pluto in the natal chart correlates to the soul itself and its deepest, unconscious desires across lifetimes**
+- Pluto by sign and house describes **what kinds of desires have driven the soul** and what kinds of experiences it repeatedly gravitated toward
+- Pluto correlates with **deepest unconscious security needs and complexes** — where we cling, fear loss, or feel threatened at a primitive level
+- **Evolutionary Astrology starts every interpretation from Pluto and builds outward**
+
+### The Two Trinities
+
+Green formalizes the reading into two triads:
+
+**Trinity of the Past (Evolutionary Past):**
+
+| Component | Meaning |
+|---|---|
+| **Pluto** | Core past-life desires that shaped prior incarnations |
+| **South Node of the Moon** | Operational mode — how those desires were expressed behaviorally |
+| **Ruler of the South Node** | Specific archetypal themes, circumstances, and character patterns |
+
+**Trinity of the Future (Evolutionary Intentions Now):**
+
+| Component | Meaning |
+|---|---|
+| **Pluto's Polarity Point (PPP)** | The point opposite Pluto — archetypes that must be embraced to evolve beyond the past |
+| **North Node of the Moon** | New operational mode the soul must develop |
+| **Ruler of the North Node** | Archetypal patterns and life areas through which the future unfolds |
+
+### Pluto's Polarity Point (PPP)
+
+The **Pluto Polarity Point** is simply the degree opposite Pluto (e.g., Pluto 2° Aries → PPP 2° Libra):
+
+- Shows **what must be developed to balance Pluto's existing center of gravity**
+- If Pluto shows where the soul has **over-focused**, the PPP shows the **archetypal counterweight**
+- Actively engaging the house/sign of the PPP is how the soul **evolves out of repetitive past-life patterns**
+
+### Green's Chart Reading Sequence
+
+1. **Pluto by sign and house** — core desires and evolutionary themes
+2. **South Node by sign and house** — ingrained behavioral/emotional habits
+3. **Ruler of the South Node** — where past habits intensified
+4. **Pluto's polarity point** — what must be developed to evolve
+5. **North Node by sign and house** — qualities and situations that foster growth
+6. **Ruler of the North Node** — specific arenas for evolutionary integration
+
+Only after this framework does Green move to other planets, aspects, transits, and progressions.
+
+### Pluto Conjunct South Node
+
+A key karmic signature:
+- Strong indicator of **heavy past-life intensity**: power, control, trauma, life-and-death situations
+- The soul has been **stuck in a Plutonian pattern** (obsession, manipulation, victim-perpetrator dynamics)
+- Under pressure to evolve beyond it
+- Can be slow/worked through fully only over key milestones, like the **second Saturn return**
+
+### Planetary Nodes
+
+Green pioneered the use of **planetary nodes** (nodes of Pluto, Saturn, Uranus, etc.) for additional layers:
+- **South planetary node**: collective and personal past for that archetype
+- **North planetary node**: collective and personal future trend
+
+---
+
+## Retrograde Planets and Karma
+
+*Source: Martin Schulman, Karmic Astrology Vol. II — Retrogrades and Reincarnation*
+
+Key principles from Schulman's second volume:
+
+- **Retrograde planets** are interpreted as **breaking the "time barrier"** — carrying karmic material from other lifetimes into the present incarnation
+- Schulman departs from traditional good/bad meanings of retrogrades
+- He describes **three vibrational modes** in which retrograde planets can operate
+- Each planet is discussed in all signs and houses
+- The ISBN for this volume: 978-0-87728-345-4
+
+---
+
+## The Part of Fortune
+
+*Source: Martin Schulman, Karmic Astrology Vol. III — Joy and the Part of Fortune*
+
+- The **Part of Fortune** (Arabic Part) is explored from a karmic perspective
+- Focus on the **intersection of joy, karmic fulfillment, and the soul's purpose**
+- Deals with how karmic themes play out in practical, lived experience
+- Shows the area where karmic reaping is most directly experienced
+
+---
+
+## Other Karmic Indicators in the Chart
+
+Beyond the nodes, Saturn, and Pluto, karmic astrologers also examine:
+
+- **12th House placements** — spiritual and collective karma, unconscious patterns, past-life memories
+- **Saturn aspects** to personal planets — specific karmic stories
+- **Chiron** — the "wounded healer," often interpreted as a pointer to the deepest karmic wound
+- **Vertex** — fated encounters and relationships
+- **Fixed stars** conjunct planets — specific karmic signatures depending on the star
+- **Asteroids** (for some practitioners): especially Juno (relationship karma), Vesta (devotional karma), and Pallas (wisdom karma)
+
+### Nodes in Synastry (Relationship Karma)
+
+- **Another person's Saturn or Pluto on your South Node** often indicates powerful past-life bonds
+- Can correlate with themes of control, responsibility, or unfinished business between souls
+- A sense of "I've known you forever" combined with heaviness or intimacy
+- **Node conjunctions** between two charts suggest fated connections and karmic lessons through relationship
+
+---
+
+## The Four Volumes of Martin Schulman's Karmic Astrology
+
+Martin Schulman authored one of the most influential karmic astrology series, published by Samuel Weiser Inc.:
+
+### Volume I: *The Moon's Nodes and Reincarnation* (1975)
+- **Focus**: Lunar nodes by sign and house, aspects to the nodes, and reincarnation themes
+- **Contents**: Introductory chapters on reincarnation and karma; full delineation of nodes by sign and house; aspects to the nodes; sample chart delineations; nodal positions from 1850–2000
+- **The backbone of karmic interpretation**
+
+### Volume II: *Retrogrades and Reincarnation* (1980/1984)
+- **Focus**: Retrograde planets as karmic indicators — three vibrational modes
+- ISBN: 978-0-87728-345-4 | 204 pages
+
+### Volume III: *Joy and the Part of Fortune*
+- **Focus**: The Part of Fortune and "Joy" from a karmic perspective
+- How karmic themes play out in practical life
+
+### Volume IV: *The Karma of the Now*
+- **Focus**: Contemporary life and "the karma of the present moment"
+- Rare continuation of the series
+
+### Where to Find
+- Used/physical copies: AbeBooks, ThriftBooks, Amazon marketplace, eBay
+- Internet Archive holds a scanned edition of Volume 1
+
+---
+
+## Key Authors and Resources
+
+### Books
+- **Martin Schulman** — *Karmic Astrology*, Vols. I–IV (Samuel Weiser Inc.)
+- **Jan Spiller** — *Astrology for the Soul* — North Node by sign and house interpretations
+- **Jeffrey Wolf Green** — *Pluto: The Evolution of the Soul* and Evolutionary Astrology workshop materials
+- **Steven Forrest** — *The Inner Sky* — evolutionary approach to natal astrology
+- **Howard Sasportas** — *The Twelve Houses* — karmic house significations
+- **Dane Rudhyar** — *The Astrology of Personality* and *The Lunation Cycle* — humanistic/astrological foundations
+- **Bernadette Brady** — *Predictive Astrology: The Eagle and the Lark* — fixed star karmic influences
+- **Donna Cunningham** — *An Astrological Guide to Self-Awareness*
+
+### Schools and Websites
+- **School of Evolutionary Astrology** (Jeffrey Wolf Green's system) — schoolofevolutionaryastrology.com
+- **Jan Spiller's Legacy** — janspiller.com
+- **WildWitchWest** — wildwitchwest.com (articles on nodes and relationship astrology)
+- **Almanac.com** — lunar nodes explained article
+- **Curtis Burns** — lunar sign resource
+
+---
+
+## Sources and Further Reading
+
+1. WildWitchWest — *Relationship Astrology Techniques: Synastry, Composite, Davison, and Coalescent Charts*
+   https://www.wildwitchwest.com/single-post/relationship-astrology-techniques-synastry-composite-davison-and-coalescent-charts
+
+2. WildWitchWest — *Lunar Nodes: Karmic Balancing Act*
+   https://www.wildwitchwest.com/single-post/2016/07/13/lunar-nodes-karmic-balancing-act
+
+3. Almanac.com — *Lunar Nodes Explained: What Your Past Reveals About Your Future Path*
+   https://www.almanac.com/lunar-nodes-explained-what-your-past-reveals-about-your-future-path
+
+4. Martin Schulman — *Karmic Astrology, Vol. 1: The Moon's Nodes and Reincarnation* (Samuel Weiser Inc., 1975)
+   https://archive.org/details/karmicastrology00schu
+
+5. Martin Schulman — *Karmic Astrology, Vol. II: Retrogrades and Reincarnation* (1984)
+   ISBN: 978-0-87728-345-4
+
+6. Martin Schulman — *Karmic Astrology, Vol. III: Joy and the Part of Fortune*
+
+7. Martin Schulman — *Karmic Astrology: The Karma of the Now*
+
+8. Jan Spiller — *Astrology for the Soul*
+   https://www.janspiller.com/janslegacy/
+
+9. Jeffrey Wolf Green — Evolutionary Astrology materials
+   https://schoolofevolutionaryastrology.com
+
+10. Advanced Astrology subreddit — community discussions on karmic astrology
+    https://www.reddit.com/r/Advancedastrology/

+ 108 - 0
mcp-wishlists/karmic-astrology.md

@@ -0,0 +1,108 @@
+# Karmic Astrology — Astro MCP Feature Wishlist
+
+> What the astro MCP should compute/support to enable karmic interpretation by agents.
+
+## Already Supported (verify)
+
+- [x] Natal chart calculation (all planets, houses, angles)
+- [x] Lunar nodes (North/South) — sign, house, degree
+- [x] Planetary positions with retrograde flags
+- [x] Aspects between planets (conjunction, opposition, square, trine, sextile)
+- [x] House systems (Placidus, Equal, Whole Sign)
+- [x] Transit chart calculation
+- [x] Synastry chart (interchart aspects, house overlays)
+- [x] Composite chart calculation
+- [x] Person database (store/retrieve birth data)
+
+## Needed for Karmic Interpretation
+
+### 1. Node-Related Computations
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Node sign/house lookup** | Return North & South Node sign, house, degree for any natal chart | High |
+| **Node axis aspects** | Identify all aspects from natal planets to the nodal axis (conjunction, square, opposition, trine, sextile) with orbs | High |
+| **Node conjunct planets** | Flag planets conjunct South Node (past-life gifts/blockages) and North Node (growth direction) | High |
+| **Node square planets** | Flag planets square the nodal axis ("skipped steps" — unresolved past-life issues) | Medium |
+| **Nodal axis by house** | Return which house the nodal axis falls in (life area of karmic focus) | High |
+
+### 2. Saturn Karmic Computations
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Saturn sign/house** | Return Saturn's sign, house, degree, retrograde status | High |
+| **Saturn aspects to personal planets** | Identify Saturn hard aspects (conjunction, square, opposition) to Sun, Moon, Venus, Mars | High |
+| **Saturn retrograde flag** | Flag Saturn retrograde (unfinished past-life duties) | Medium |
+| **Saturn-Node aspects** | Identify aspects between Saturn and the nodal axis (karmic contracts, blocks) | High |
+| **Saturn return timing** | Calculate current/past/future Saturn return dates | Medium |
+
+### 3. Pluto Evolutionary Computations
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Pluto sign/house** | Return Pluto's sign, house, degree | High |
+| **Pluto polarity point** | Calculate the point opposite Pluto (PPP) — sign, house, degree | High |
+| **Pluto-South Node conjunction** | Flag Pluto conjunct South Node (intense past-life pattern) | High |
+| **Pluto aspects to nodes** | Identify all aspects from Pluto to the nodal axis | Medium |
+| **Pluto aspects to personal planets** | Identify Pluto hard aspects to Sun, Moon, Venus, Mars | Medium |
+
+### 4. Retrograde Planet Analysis
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Retrograde planet list** | Return all retrograde planets in a natal chart with their signs/houses | High |
+| **Retrograde personal planets** | Flag retrograde Mercury, Venus, Mars (karmic communication/love/action themes) | Medium |
+| **Retrograde outer planets** | Flag retrograde Jupiter, Saturn, Uranus, Neptune, Pluto | Low |
+
+### 5. 12th House / Spiritual Karma
+
+| Feature | Description | Priority |
+|---|---|---|
+| **12th house cusp sign** | Return the sign on the 12th house cusp | Medium |
+| **12th house planets** | List any planets in the 12th house | Medium |
+| **12th house ruler** | Identify the ruler of the 12th house and its sign/house/aspects | Low |
+
+### 6. Karmic Synastry (Relationship Karma)
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Saturn-Node synastry** | Identify when one person's Saturn aspects the other's nodal axis | High |
+| **Pluto-Node synastry** | Identify when one person's Pluto aspects the other's nodal axis | High |
+| **Node conjunctions in synastry** | Flag when one person's planet conjuncts the other's North or South Node | High |
+| **Saturn personal planet synastry** | Flag Saturn to Sun/Moon/Venus/Mars interchart aspects | High |
+| **Karmic relationship summary** | Generate a summary of karmic indicators between two charts | Medium |
+
+### 7. Composite Chart Karmic Features
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Composite Node axis** | Return composite chart nodal axis sign/house | Medium |
+| **Composite Saturn** | Return composite Saturn sign, house, aspects | Medium |
+| **Composite Pluto** | Return composite Pluto sign, house, aspects | Medium |
+
+### 8. Transit Triggers for Karmic Periods
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Saturn transit to natal nodes** | Identify when transiting Saturn aspects the natal nodal axis | High |
+| **Pluto transit to natal nodes** | Identify when transiting Pluto aspects the nodal axis | Medium |
+| **Node return timing** | Calculate when the transiting nodal axis returns to natal position (~18.6 year cycle) | Medium |
+| **Eclipse activation of nodes** | Flag eclipses conjunct the natal nodal axis | Low |
+
+## Nice-to-Have
+
+| Feature | Description |
+|---|---|
+| **Part of Fortune calculation** | Arabic Part of Fortune — sign, house, aspects |
+| **Chiron position** | Chiron sign, house, aspects (wounded healer / deepest karmic wound) |
+| **Vertex axis** | Vertex sign/house (fated encounters) |
+| **Fixed star conjunctions** | Major fixed stars conjunct natal planets (karmic signatures) |
+| **Planetary nodes** | Nodes of Pluto, Saturn, etc. (Jeffrey Wolf Green's evolutionary astrology) |
+| **Karmic chart summary** | Auto-generated text summary of the main karmic themes in a natal chart |
+
+## API Design Notes
+
+- All features should accept a `person_id` from the database or raw birth data
+- Orb configuration should be customizable (default: 5° for personal planets, 3° for outer)
+- Return structured JSON with sign, house, degree, retrograde flag, aspect type, orb
+- Support house system parameter (Placidus, Equal, Whole Sign)

+ 153 - 0
mcp-wishlists/natal-astrology.md

@@ -0,0 +1,153 @@
+# Natal Astrology — Astro MCP Feature Wishlist
+
+> What the astro MCP should compute/support to enable natal chart interpretation by agents.
+
+## Already Supported (verify)
+
+- [x] Natal chart calculation (all planets, houses, angles, aspects)
+- [x] Planetary positions with sign, degree, retrograde flag
+- [x] House systems (Placidus, Equal, Whole Sign)
+- [x] Aspect calculation (conjunction, opposition, square, trine, sextile) with orbs
+- [x] Person database (store/retrieve birth data)
+- [x] Transit chart calculation
+- [x] Transit preview (daily aspect snapshots with significance scoring)
+- [x] Lunar nodes (North/South)
+- [x] Synastry chart
+- [x] Composite chart
+- [x] Multiple birth data formats (ISO 8601 with timezone offset)
+
+## Enhancements Needed for Natal Interpretation
+
+### 1. Chart Overview / Summary
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Element balance report** | Count planets by element (fire/earth/air/water) with percentages | High |
+| **Modality balance report** | Count planets by modality (cardinal/fixed/mutable) with percentages | High |
+| **Hemisphere emphasis** | Report which hemisphere (upper/lower/east/west) has most planets | Medium |
+| **Chart shape detection** | Identify bundle, bowl, bucket, splash, locomotive, seesaw, splay patterns | Medium |
+| **Stellium detection** | Flag any sign or house with 3+ planets | High |
+| **Empty house list** | Report which houses have no planets | Low |
+
+### 2. Big Three Enhancement
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Chart ruler identification** | Return the planet ruling the Ascendant sign, with its sign/house/aspects | High |
+| **Sun-Moon aspect** | Specifically return the aspect (if any) between Sun and Moon | Medium |
+| **Sun/Moon phase** | Return the lunar phase at birth (new moon, waxing, full, waning) | Low |
+
+### 3. Aspect Pattern Detection
+
+| Feature | Description | Priority |
+|---|---|---|
+| **T-square detection** | Identify T-square patterns: two planets in opposition, both squaring a third planet | High |
+| **Grand trine detection** | Identify grand trine patterns: three planets in trine, same element | High |
+| **Grand cross detection** | Identify grand cross patterns: four planets forming two oppositions and four squares | High |
+| **Yod detection** | Identify yod patterns: two sextile planets both quincunx a third | Medium |
+| **Aspect pattern summary** | Return all major aspect patterns found in the chart with involved planets/houses | High |
+
+### 4. Aspect Prioritization
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Top aspects by orb** | Return the N tightest aspects (smallest orbs) in the chart | High |
+| **Aspects to Big Three** | Return all aspects involving Sun, Moon, or Ascendant | High |
+| **Aspects to chart ruler** | Return all aspects involving the chart ruler | Medium |
+| **Applying vs. separating** | Flag whether each aspect is applying or separating | Medium |
+
+### 5. House Analysis
+
+| Feature | Description | Priority |
+|---|---|---|
+| **House cusp signs** | Return the sign on each house cusp | High |
+| **House rulers** | For each house, return the ruling planet and its sign/house/condition | Medium |
+| **Planets in houses** | Group planets by house for quick house-themed reading | High |
+| **Angular/succedent/cadent count** | Count planets by house type | Medium |
+
+### 6. Sign Analysis
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Planets in signs** | Group planets by sign for quick sign-themed reading | High |
+| **Sign dominance** | Identify which sign has the most planets (if any) | Low |
+
+### 7. Retrograde Report
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Retrograde planet list** | Return all retrograde planets with sign/house | Medium |
+| **Retrograde emphasis** | Flag charts with 3+ retrograde planets | Low |
+
+### 8. Transit Integration for Natal Context
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Current transits to natal** | Return current transiting aspects to natal planets with orbs and applying/separating | High |
+| **Upcoming major transits** | Return transits to natal Sun/Moon/Ascendant/chart ruler within next N days | Medium |
+| **Saturn return flag** | Flag if the person is currently in their Saturn return window (~28-30 years) | Medium |
+| **Jupiter return flag** | Flag if the person is currently in their Jupiter return window (~12 years) | Low |
+| **Eclipse proximity** | Flag if any natal planet is within 5° of the current eclipse axis | Low |
+
+### 9. Chart Synthesis Output (Nice-to-Have)
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Auto narrative summary** | Generate a structured text summary of the natal chart's main themes | Low |
+| **Strength/challenge list** | Based on aspects, auto-generate a list of natural strengths and growth edges | Low |
+| **Interpretation prompts** | Return structured prompts an agent can use to build a reading | Medium |
+
+## API Design Notes
+
+### Chart Overview Output
+```json
+{
+  "element_balance": { "fire": 3, "earth": 2, "air": 4, "water": 3 },
+  "modality_balance": { "cardinal": 4, "fixed": 5, "mutable": 3 },
+  "hemisphere": { "upper": 7, "lower": 5, "east": 6, "west": 6 },
+  "chart_shape": "bowl",
+  "stelliums": [{"sign": "Scorpio", "planets": ["Sun", "Mercury", "Venus"]}],
+  "empty_houses": [3, 9]
+}
+```
+
+### Aspect Pattern Output
+```json
+{
+  "patterns": [
+    {
+      "type": "T-square",
+      "planets": ["Moon", "Mars", "Saturn"],
+      "apex": "Saturn",
+      "houses": [4, 7, 10],
+      "signs": ["Cancer", "Libra", "Capricorn"],
+      "modality": "cardinal"
+    }
+  ]
+}
+```
+
+### Top Aspects Output
+```json
+{
+  "top_aspects": [
+    {
+      "planet_a": "Sun",
+      "planet_b": "Moon",
+      "aspect": "trine",
+      "orb": 2.3,
+      "applying": true,
+      "sign_a": "Leo",
+      "sign_b": "Sagittarius",
+      "house_a": 5,
+      "house_b": 9
+    }
+  ]
+}
+```
+
+## House System Notes
+
+- Default: Placidus (most common in modern Western astrology)
+- Support: Equal House, Whole Sign (important for Vedic/traditional work)
+- All house-related features should respect the selected house system

+ 110 - 0
mcp-wishlists/relationship-astrology.md

@@ -0,0 +1,110 @@
+# Relationship Astrology — Astro MCP Feature Wishlist
+
+> What the astro MCP should compute/support to enable relationship chart interpretation by agents.
+
+## Already Supported (verify)
+
+- [x] Natal chart calculation
+- [x] Synastry chart (interchart aspects, house overlays)
+- [x] Composite chart calculation
+- [x] Person database
+- [x] Aspect calculation with configurable orbs
+- [x] House systems (Placidus, Equal, Whole Sign)
+- [x] Transit chart calculation
+
+## Needed for Relationship Interpretation
+
+### 1. Davison Chart
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Davison chart calculation** | Calculate the midpoint-in-time-and-space chart for two people (average of birth dates, times, locations) | High |
+| **Davison planet positions** | Return all planetary positions, signs, houses, angles for the Davison chart | High |
+| **Davison aspects** | Calculate aspect patterns within the Davison chart | High |
+| **Davison transits** | Calculate transits to the Davison chart (for timing relationship events) | Medium |
+| **Davison progressions** | Calculate secondary progressions to the Davison chart | Low |
+
+### 2. Synastry Enhancements
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Interchart aspects matrix** | Full matrix of aspects between all planets of Person A and Person B, with orbs | High |
+| **House overlay report** | For each planet in Chart A, report which house it falls in Chart B (and vice versa) | High |
+| **Top synastry aspects** | Return the 10-15 tightest/most significant interchart aspects, ranked by orb and significance | High |
+| **Relationship significator aspects** | Specifically identify Venus-Mars, Moon-Venus, Sun-Moon, Sun-Saturn interchart aspects | High |
+| **Saturn synastry aspects** | Specifically flag all Saturn interchart aspects to personal planets and nodes | High |
+| **Node synastry aspects** | Specifically flag all interchart aspects to the nodal axes | High |
+
+### 3. Composite Chart Enhanced Interpretation
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Composite planet meanings** | Return structured data for each composite planet (sign, house, aspects) | Medium |
+| **Composite aspect list** | Return all aspects within the composite chart with orbs (tight orbs ≤3°) | Medium |
+| **Composite angular planets** | Flag planets conjunct the composite angles (AC, MC, DC, IC) | Medium |
+| **Composite chart ruler** | Identify and return the chart ruler's condition (sign, house, aspects) | Medium |
+| **Composite unaspected planets** | Flag planets with no major aspects (relationship blind spots) | Medium |
+| **Composite house emphasis** | Calculate which houses have the most planets (thematic emphasis) | Medium |
+| **Composite transit report** | Calculate transits to composite chart planets/angles for timing | Medium |
+
+### 4. Coalescent Chart (Nice-to-Have)
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Coalescent chart calculation** | Implement Lawrence Grinnell's harmonic coalescent method | Low |
+| **Harmonic arc calculation** | Calculate shortest arcs between planet pairs and derive harmonic number | Low |
+| **Coalescent transit sensitivity** | Identify transits that strongly activate the coalescent chart | Low |
+
+### 5. Karmic Relationship Synthesis
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Karmic synastry summary** | Combine synastry + composite + Davison karmic indicators into a structured report | Medium |
+| **Relationship type classifier** | Based on chart data, classify relationship type indicators (romantic, business, family, friendship, karmic) | Low |
+| **Compatibility score** | Generate a structured compatibility assessment (not a single number, but category scores: emotional, sexual, intellectual, commitment, spiritual) | Low |
+
+### 6. Relationship Timing
+
+| Feature | Description | Priority |
+|---|---|---|
+| **Composite transit preview** | Transit-to-composite chart aspect snapshots over a date range | Medium |
+| **Davison transit preview** | Transit-to-Davison chart aspect snapshots over a date range | Medium |
+| **Synthesis transit preview** | Combine transit-to-natal (both people) + transit-to-composite/Davison for relationship timing | Low |
+| **Eclipse activation of relationship charts** | Flag eclipses hitting composite/Davison angles or personal planets | Low |
+
+## API Design Notes
+
+### Davison Chart Input
+```
+person1_id: string (from database)
+person2_id: string (from database)
+  OR
+person1_datetime, person1_latitude, person1_longitude
+person2_datetime, person2_latitude, person2_longitude
+house_system: "placidus" | "equal" | "whole_sign"
+orb_limits: optional per-aspect-type orb configuration
+```
+
+### Synastry Enhancement Input
+```
+person1_id / person2_id (or raw birth data)
+include_house_overlays: boolean
+include_aspect_matrix: boolean
+max_orb: number (default: 5)
+significance_filter: optional minimum significance score
+```
+
+### Output Format
+- Structured JSON with sections: `interchart_aspects`, `house_overlays`, `composite_planets`, `composite_aspects`, `davison_planets`, `davison_aspects`
+- Each aspect: `{planet_a, planet_b, aspect_type, orb, applying/separating, significance}`
+- Each house overlay: `{planet, owner_chart, house, house_meaning}`
+- Summary fields: `top_aspects`, `saturn_contacts`, `node_contacts`, `venus_mars_contacts`
+
+## Relationship Reading Workflow (for agents)
+
+1. **Synastry** → interchart aspects + house overlays (interaction patterns, chemistry, friction)
+2. **Composite chart** → the relationship's identity, structure, public face
+3. **Davison chart** → the relationship's inner experience, emotional tone, long-term evolution
+4. **Karmic overlay** → Saturn/Pluto/Node contacts across all three layers
+5. **Transit timing** → transits to composite/Davison for relationship milestones
+6. **Synthesis** → repeated themes across all layers = core relationship narrative

+ 399 - 0
natal-astrology.md

@@ -0,0 +1,399 @@
+# Natal Astrology — A Comprehensive Reference
+
+> Compiled from multiple online sources, June 2026.
+> Primary sources: Almanac.com, Perplexity AI synthesis, Cafe Astrology, Steven Forrest, industry-standard interpretation frameworks.
+
+---
+
+## Table of Contents
+
+1. [What Is a Natal Chart?](#what-is-a-natal-chart)
+2. [The Big Three: Sun, Moon, Ascendant](#the-big-three-sun-moon-ascendant)
+3. [The Planets — Functions and Keywords](#the-planets--functions-and-keywords)
+4. [The Signs — How Energy Behaves](#the-signs--how-energy-behaves)
+5. [The Houses — Life Areas](#the-houses--life-areas)
+6. [Aspects — How Planets Interact](#aspects--how-planets-interact)
+7. [Aspect Patterns — Major Geometries](#aspect-patterns--major-geometries)
+8. [Chart Shapes and Overall Patterns](#chart-shapes-and-overall-patterns)
+9. [Step-by-Step Reading Order](#step-by-step-reading-order)
+10. [Synthesis: Building the Narrative](#synthesis-building-the-narrative)
+11. [Quick Interpretation Templates](#quick-interpretation-templates)
+12. [Sources](#sources)
+
+---
+
+## What Is a Natal Chart?
+
+A **natal chart** (or birth chart) is a snapshot of the sky at the exact moment and location of a person's birth. It maps the positions of the Sun, Moon, planets, and key points against the 12 zodiac signs and 12 houses.
+
+The chart is the foundational tool of astrology — a symbolic map of personality, potential, life themes, and timing. It shows **who you are**, **what you need**, **where you're headed**, and **what challenges and gifts you carry**.
+
+### Key Components
+
+| Component | Represents |
+|---|---|
+| **Planets** | Psychological functions — what is happening |
+| **Signs** | Style and flavor — how the energy behaves |
+| **Houses** | Life areas — where the action takes place |
+| **Aspects** | Relationships between functions — how planets interact |
+| **Angles** | Core axes of identity, home, partnership, career |
+
+---
+
+## The Big Three: Sun, Moon, Ascendant
+
+The **Big Three** are the foundation of any natal reading. They provide the overall "vibe" of the chart.
+
+### Sun — Core Identity
+
+- **Represents**: Core self, ego, life force, purpose, vitality, will
+- **Sign**: *How* you shine and express your identity
+- **House**: *Where* in life you want to shine and seek recognition
+- **Questions to ask**: What qualities do I naturally embody and want to grow into? In what area of life do I seek purpose?
+
+### Moon — Emotional World
+
+- **Represents**: Emotional needs, instincts, body wisdom, habits, memory, comfort, the inner self
+- **Sign**: Your emotional style and what makes you feel safe
+- **House**: Life area that strongly affects mood and security
+- **Questions to ask**: What do I need to feel emotionally secure? How do I instinctively react when stressed?
+
+### Ascendant (Rising Sign) — Persona
+
+- **Represents**: Outward persona, first impression, "mask," approach to life, how you initiate
+- **Sign**: The style of your behavior and appearance
+- **Ruler of the Ascendant** (chart ruler): Its sign, house, and aspects add crucial information about life path and self-image
+- **Questions to ask**: How do others initially experience me? How do I naturally approach new beginnings?
+
+---
+
+## The Planets — Functions and Keywords
+
+Planets are the **psychological functions** or "actors" in the chart. Each represents a different domain of experience.
+
+### Personal Planets
+
+| Planet | Keywords | Domain |
+|---|---|---|
+| **Sun** | Identity, will, purpose, vitality, ego, consciousness | Core self |
+| **Moon** | Emotions, instincts, needs, habits, comfort, body | Inner world |
+| **Mercury** | Thinking, communication, learning, decision-making | Mind |
+| **Venus** | Love, attraction, values, pleasure, aesthetics, harmony | Relationships & values |
+| **Mars** | Drive, assertiveness, conflict, sexuality, initiative | Action & desire |
+
+### Social Planets
+
+| Planet | Keywords | Domain |
+|---|---|---|
+| **Jupiter** | Growth, expansion, faith, optimism, luck, wisdom | Opportunity & belief |
+| **Saturn** | Discipline, responsibility, fear, lessons, limits, mastery | Structure & maturity |
+
+### Outer Planets (Generational + Personal)
+
+| Planet | Keywords | Domain |
+|---|---|---|
+| **Uranus** | Originality, rebellion, sudden change, innovation, freedom | Disruption & liberation |
+| **Neptune** | Dreams, ideals, spirituality, fantasy, confusion, dissolution | Imagination & transcendence |
+| **Pluto** | Power, transformation, deep psychology, taboo, death & rebirth | Evolution & power |
+
+### Lunar Nodes
+
+| Point | Keywords | Domain |
+|---|---|---|
+| **North Node** | Growth direction, life purpose, unfamiliar territory | Where you're headed |
+| **South Node** | Past patterns, comfort zone, innate talents | Where you're coming from |
+
+### How to Read Any Planet
+
+1. **Sign**: *How* that function behaves (fiery vs. cautious, direct vs. subtle)
+2. **House**: *Where* that planet's themes show up most strongly
+3. **Aspects**: Whether that function is supported, challenged, or intensified by other functions
+
+**Formula**: Planet (what) + Sign (how) + House (where) + Aspects (modifiers) = interpretation
+
+---
+
+## The Signs — How Energy Behaves
+
+The 12 zodiac signs describe the **style** or **flavor** of each planet's expression. Each sign belongs to an **element** and a **modality**.
+
+### Elements
+
+| Element | Signs | Quality |
+|---|---|---|
+| **Fire** | Aries, Leo, Sagittarius | Inspiring, energetic, passionate, action-oriented |
+| **Earth** | Taurus, Virgo, Capricorn | Practical, grounded, material, steady |
+| **Air** | Gemini, Libra, Aquarius | Intellectual, communicative, social, conceptual |
+| **Water** | Cancer, Scorpio, Pisces | Emotional, intuitive, empathic, deep |
+
+### Modalities
+
+| Modality | Signs | Quality |
+|---|---|---|
+| **Cardinal** | Aries, Cancer, Libra, Capricorn | Initiating, leading, action-oriented, seasonal beginnings |
+| **Fixed** | Taurus, Leo, Scorpio, Aquarius | Stabilizing, determined, persistent, resistant to change |
+| **Mutable** | Gemini, Virgo, Sagittarius, Pisces | Adaptable, flexible, transitional, versatile |
+
+### Sign Keywords (Quick Reference)
+
+| Sign | Element | Modality | Core Keywords |
+|---|---|---|---|
+| Aries | Fire | Cardinal | Pioneering, impulsive, courageous, direct |
+| Taurus | Earth | Fixed | Steady, sensual, loyal, possessive |
+| Gemini | Air | Mutable | Curious, communicative, versatile, scattered |
+| Cancer | Water | Cardinal | Nurturing, protective, emotional, home-oriented |
+| Leo | Fire | Fixed | Creative, dramatic, generous, proud |
+| Virgo | Earth | Mutable | Analytical, service-oriented, precise, critical |
+| Libra | Air | Cardinal | Diplomatic, relational, balanced, indecisive |
+| Scorpio | Water | Fixed | Intense, transformative, secretive, powerful |
+| Sagittarius | Fire | Mutable | Philosophical, adventurous, optimistic, blunt |
+| Capricorn | Earth | Cardinal | Ambitious, disciplined, responsible, cautious |
+| Aquarius | Air | Fixed | Original, humanitarian, detached, innovative |
+| Pisces | Water | Mutable | Compassionate, imaginative, spiritual, boundaryless |
+
+---
+
+## The Houses — Life Areas
+
+The 12 houses represent **where** in life the planetary energies are experienced. They are the arenas of life.
+
+### House Classification
+
+| Type | Houses | Quality |
+|---|---|---|
+| **Angular** | 1, 4, 7, 10 | Cornerstones — most powerful, action-oriented |
+| **Succedent** | 2, 5, 8, 11 | Stabilizing — security, resources, creativity |
+| **Cadent** | 3, 6, 9, 12 | Transitional — learning, service, spirituality |
+
+### The 12 Houses
+
+| House | Name | Domain | Keywords |
+|---|---|---|---|
+| **1st** | Self / Identity | Body, appearance, persona, first impressions, how you initiate | "This is who I am" |
+| **2nd** | Values / Resources | Money, possessions, income, self-worth, what you value | "This is what I have" |
+| **3rd** | Communication / Mind | Thinking, learning, speech, siblings, neighbors, short trips | "This is how I think" |
+| **4th** | Home / Roots | Family, upbringing, emotional foundation, private life, home | "This is where I come from" |
+| **5th** | Creativity / Pleasure | Romance, dating, fun, hobbies, children, self-expression | "This is what I create" |
+| **6th** | Work / Health | Daily routines, service, co-workers, health, habits | "This is how I serve" |
+| **7th** | Partnerships | One-to-one relationships, marriage, business partners, open enemies | "This is who I meet" |
+| **8th** | Transformation / Shared Resources | Joint finances, debts, inheritance, intimacy, sex, death & rebirth | "This is what we share" |
+| **9th** | Expansion / Higher Mind | Higher education, religion, spirituality, long travel, worldview | "This is what I believe" |
+| **10th** | Career / Public Role | Career, reputation, achievements, authority, life direction | "This is what I achieve" |
+| **11th** | Community / Future | Friends, groups, networks, hopes, goals, social causes | "This is where I belong" |
+| **12th** | Inner World / Retreat | Subconscious, solitude, hidden matters, spirituality, self-undoing | "This is what's hidden" |
+
+### Empty Houses
+
+An empty house doesn't mean nothing happens there — it simply suggests that area may not be a major **focus** of identity. Transits will activate every house over time.
+
+---
+
+## Aspects — How Planets Interact
+
+Aspects are the **angular relationships** between planets. They describe how different psychological functions communicate, support, or challenge each other.
+
+### Major Aspects
+
+| Aspect | Angle | Orb (typical) | Meaning |
+|---|---|---|---|
+| **Conjunction** | 0° | 5°–8° | Fusion, intensification — energies blend and amplify |
+| **Sextile** | 60° | 3°–5° | Cooperation, opportunity — gifts that require active use |
+| **Square** | 90° | 5°–8° | Friction, tension, conflict — demands effort and growth |
+| **Trine** | 120° | 5°–8° | Harmony, ease, natural talent — can be underused |
+| **Opposition** | 180° | 5°–8° | Polarity, projection — requires balance and compromise |
+
+### Aspect Interpretation Formula
+
+1. Identify the **two planets** and what they represent
+2. Note the **type of aspect** (supportive vs. challenging)
+3. Look at the **houses** they occupy — which life areas are involved?
+4. Consider the **signs** — they color the tone of the interaction
+
+**Template**: "Planet A (function) in sign X interacts with Planet B (function) in sign Y by [aspect] — creating [dynamic] between [life area A] and [life area B]."
+
+### Aspects to the Big Three
+
+Aspects involving the **Sun, Moon, or Ascendant** are especially important — they directly modify core identity, emotional needs, or outward persona.
+
+### Applying vs. Separating Aspects
+
+- **Applying**: The faster planet is moving toward the exact aspect — energy is building
+- **Separating**: The faster planet is moving away — energy is releasing
+- **Exact**: The aspect is at its peak influence
+
+---
+
+## Aspect Patterns — Major Geometries
+
+When three or more planets form a recognizable geometric pattern, it creates a **thematic emphasis** in the chart.
+
+### T-Square
+
+- **Shape**: Two planets oppose each other; both square a third ("apex") planet
+- **Meaning**: Stress plus potential. The opposition and squares generate friction that can be channeled into achievement. The apex planet is the focal point — where the tension concentrates.
+- **Interpretation**: Identify the apex planet (sign, house) — this is where the person is pushed to grow. The missing "fourth corner" (opposite the apex) represents the area of life that needs conscious development.
+
+### Grand Trine
+
+- **Shape**: Three planets form a triangle of trines (usually all in the same element)
+- **Meaning**: Natural talent, harmony, ease of expression. A flow of energy that comes naturally.
+- **Caution**: Can become *too comfortable* — the person may not develop discipline or ambition in this area without supporting challenging aspects.
+
+### Grand Cross
+
+- **Shape**: Four planets form two oppositions and four squares (usually all in the same modality)
+- **Meaning**: Major inner conflict, high pressure, endurance-testing energy. The person must learn balance and flexibility.
+- **Interpretation**: Each planet represents a life area in tension. The modality (cardinal/fixed/mutable) shows the style of the conflict.
+
+### Stellium
+
+- **Definition**: Three or more planets in a single sign or house
+- **Meaning**: Intense focus on that sign's themes or house's life area. A "thematic concentration."
+
+### Yod (Finger of Fate)
+
+- **Shape**: Two planets sextile each other, both quincunx (150°) to a third planet
+- **Meaning**: A fated, sensitive point. The apex planet becomes a focal point of destiny and adjustment.
+
+---
+
+## Chart Shapes and Overall Patterns
+
+### Hemisphere Emphasis
+
+| Hemisphere | Focus |
+|---|---|
+| **Upper half** (houses 7–12) | Public life, relationships, career, social engagement |
+| **Lower half** (houses 1–6) | Private life, self-development, personal foundations |
+| **Eastern half** (houses 10–3) | Self-initiated action, independence, personal agency |
+| **Western half** (houses 4–9) | Relational focus, responsiveness, partnership |
+
+### Element Balance
+
+Count planets by element to identify dominant and missing elements:
+
+- **Dominant element**: Strong theme in personality
+- **Missing element**: Underdeveloped area, often compensated through transits or house emphasis
+
+### Modality Balance
+
+- **Cardinal emphasis**: Initiating, action-oriented, seasonal leadership
+- **Fixed emphasis**: Determined, persistent, resistant to change
+- **Mutable emphasis**: Adaptable, flexible, transitional
+
+### Chart Patterns (by planetary distribution)
+
+| Pattern | Shape | Meaning |
+|---|---|---|
+| **Bundle** | Planets within 120° | Intense focus, specialized interests |
+| **Bowl** | Planets within 180° (one hemisphere) | Self-contained, focused direction |
+| **Bucket** | Planets within 180° + one "handle" planet | Focused with one area of contrast |
+| **Splash** | Planets spread around chart | Diverse interests, scattered energy |
+| **Locomotive** | Planets in 240° with a 60° gap | Driving force, the gap planet is the "engine" |
+| **Seesaw** | Two clusters on opposite sides | Life polarized between two themes |
+| **Splay** | Random clusters | Multiple independent drives |
+
+---
+
+## Step-by-Step Reading Order
+
+Use this as a **reusable checklist** for any natal chart reading.
+
+### 1. Big Picture
+- Chart shape and hemisphere emphasis
+- Element and modality balance
+- Any stelliums or major aspect patterns
+
+### 2. Ascendant and Chart Ruler
+- Ascendant sign — persona and approach
+- Chart ruler (ruler of Ascendant sign) — its sign, house, and aspects
+
+### 3. Sun and Moon
+- Sun sign + house → core identity and life purpose
+- Moon sign + house → emotional style and needs
+- Sun-Moon relationship (aspect between them)
+
+### 4. Angles and Their Rulers
+- Signs on the 1st, 4th, 7th, 10th house cusps
+- Rulers of these angles — their condition and placement
+- Planets conjunct any angle (highly emphasized)
+
+### 5. Personal Planets
+- Mercury, Venus, Mars: sign, house, key aspects
+- These describe day-to-day personality functioning
+
+### 6. Social and Outer Planets
+- Jupiter and Saturn: growth areas and challenge areas
+- Uranus, Neptune, Pluto: generational themes + personal when tightly aspecting
+
+### 7. Aspect Patterns
+- Identify T-squares, grand trines, grand crosses, yods
+- Note the planets and houses involved
+
+### 8. Synthesis
+- Identify 3–5 **recurring themes** across all layers
+- Translate technical factors into plain language
+- What does this person need to feel fulfilled?
+- How do they handle relationships, work, and conflict?
+- Where are their main strengths and life lessons?
+
+---
+
+## Synthesis: Building the Narrative
+
+The art of natal interpretation is **synthesis** — weaving individual data points into a coherent story.
+
+### What to Look For
+
+1. **Repeated themes**: If Sun, Moon, and Ascendant all emphasize relationships → relationship is a core life theme
+2. **Contradictions**: Sun in Aries (assertive) but Moon in Libra (accommodating) → inner tension between self-assertion and harmony
+3. **Compensations**: Missing fire element but Mars in the 1st house → fire expressed through action rather than identity
+4. **Dominant aspects**: Multiple squares to Saturn → a "Saturnian" life with heavy lessons around responsibility
+5. **Unaspected planets**: Function operates independently, sometimes unconsciously
+
+### Output Structure for a Natal Reading
+
+A well-structured natal interpretation should include:
+
+1. **Overview**: Core personality in 2–3 sentences
+2. **The Big Three**: Sun, Moon, Ascendant — identity, emotions, persona
+3. **Key Planet Placements**: 3–5 most significant planet-in-sign-in-house combinations
+4. **Major Aspects**: 3–5 tightest/most significant aspects with interpretation
+5. **Life Themes**: Recurring patterns across the chart
+6. **Strengths and Gifts**: Natural talents and supportive aspects
+7. **Growth Edges**: Challenging aspects and what they're teaching
+8. **Current Transits** (optional): What's activating the chart right now
+
+---
+
+## Quick Interpretation Templates
+
+### Planet in Sign in House
+
+> **[Planet]** in **[Sign]** in **[House]** → [Planet's function] expressed in the style of [Sign] through the life area of [House].
+
+**Example**: "Venus in Scorpio in the 8th house → Love and intimacy (Venus) expressed intensely and transformatively (Scorpio) through shared resources, deep bonding, and psychological merging (8th)."
+
+### Aspect Interpretation
+
+> **[Planet A]** in **[Sign X]** **[aspect]** **[Planet B]** in **[Sign Y]** → The [Planet A function] and [Planet B function] are [blended / in tension / in harmony / polarized], creating [dynamic] between [life area A] and [life area B].
+
+**Example**: "Moon in Cancer in the 4th square Mars in Libra in the 7th → Emotional needs for security and home (Moon/4th) are in tension with the drive for partnership and fairness (Mars/7th), creating a push-pull between domestic comfort and relationship demands."
+
+### Chart Ruler
+
+> The chart ruler is **[Planet]** in **[Sign]** in **[House]** → Life energy is strongly directed toward [house themes], expressed through [sign style].
+
+---
+
+## Sources
+
+- Almanac.com — *The 12 Houses of the Zodiac: What Do They Mean?*
+  https://www.almanac.com/12-houses-zodiac-what-do-they-mean
+- Perplexity AI — *Natal Astrology Interpretation Guide: Sun, Moon, Ascendant, Planets, Houses, Aspects*
+- Perplexity AI — *Natal Chart Reading Technique: Quick Reference*
+- Perplexity AI — *Aspect Patterns: T-Square, Grand Trine, Grand Cross*
+- Cafe Astrology — https://cafeastrology.com
+- Steven Forrest — *The Inner Sky*
+- Alice Bell — *The First 7 Steps in Reading a Birth Chart*
+- Benebell Wen — *How to Give a 30-Minute Birth Chart Reading*

+ 314 - 0
relationship-astrology-techniques.md

@@ -0,0 +1,314 @@
+# Relationship Astrology Techniques: Synastry, Composite, Davison, and Coalescent Charts
+
+Source: <https://www.wildwitchwest.com/single-post/relationship-astrology-techniques-synastry-composite-davison-and-coalescent-charts>
+
+While astrology can be an incredible tool for individual awareness, it can also shed a lot of clarity on relational dynamics. One of the ways I see astrology as being useful is through chart comparison. We can see what someone lights up in us or comes alive through our relating. In astrology several techniques exist for this inquiry whether it be for exploring between romantic partners, business partners, family members, or friends.
+
+[In my relationship sessions with clients](https://www.wildwitchwest.com/astrology-readings), I do my best to focus on what is the charts in a matter of fact way. The charts tell us potential, not necessarily future. I've seen thousands of charts together, some that looked amazing and two people couldn't seem to make it work...and others where I couldn't' find the relationship glue and yet they seemed incredibly happy.
+
+However, it's important to remember that compatibility in astrology is not a fixed determination. Instead, it provides a framework to understand the potential strengths and weaknesses of a relationship. Relationships thrive on communication, compromise, and mutual growth, and the birth chart aspects are just one piece of the puzzle.
+
+For this reason, with clients I often suggest not to look at anyone's chart until 6 months of knowing someone, or as a tool for reflection once it ends rather than prediction before it begins.
+
+While we often associate relationships with romance, all of the techniques below can be applied to any type of relationship, including friendships, family dynamics, and even professional collaborations. By analyzing the charts with these techniques between two individuals, astrologers can offer valuable insights into the nature of their interactions, helping them navigate challenges and capitalize on their strengths.
+
+To me relationships help us understand ourselves and others, we gain a deeper appreciation for the complexity of human connections and the potential for growth and transformation through shared experiences and astrology can help.
+
+## Synastry Charts: You v. me
+
+One of the most sought-after insights in synastry is compatibility. By examining the aspects between planets, astrologers can identify areas of resonance and tension between two people. While harmonious aspects might indicate ease and understanding, challenging aspects can suggest areas where conflicts or misunderstandings might arise.
+
+Synastry is the art and science of comparing two or more birth charts to understand the potential interactions and dynamics between individuals. Birth charts, also known as natal charts, are graphical representations of the positions of celestial bodies at the exact time and location of a person's birth. These charts are like cosmic snapshots, capturing the unique cosmic energy imprinted upon an individual at the moment of their arrival into the world.
+
+In synastry, the primary focus is on the relationships between the planets in one person's chart and the planets in another person's chart. These relationships are known as aspects. Aspects describe the angular relationships between planets, showcasing how they communicate and influence each other's energies.
+
+For instance, a conjunction occurs when two planets are close together in the same sign or house, intensifying their combined effects. A trine, on the other hand, suggests a harmonious connection, while a square signifies potential challenges and conflicts that stimulate growth and transformation. Each aspect adds a layer of complexity to the relationship, unveiling the unique dynamics that individuals bring to each other's lives.
+
+## Composite Chart: personality of the relationship, symbolic, collaboration and purpose
+
+After a certain amount of time, two charts weave something new, this is where the composite chart comes in.
+
+This composite chart, often referred to as the "relationship chart," provides a unique lens through which we can explore the dynamics and potential of any partnership, be it romantic, familial, or professional.
+
+The composite chart is born from the harmonious fusion of the birth data of two individuals, creating a singular chart that represents the energy, essence, and potential of the relationship itself. This chart is calculated by finding the midpoints between the corresponding planets and points of both individuals and constructing a new chart based on these midpoints. The result is a symbolic representation of the relationship, revealing its intrinsic qualities and challenges.
+
+Every aspect, planet, and house placement in the composite chart has a profound impact on the dynamics between the two individuals. The planetary aspects, such as conjunctions, oppositions, trines, and squares, mirror the varying degrees of compatibility and challenge present within the relationship. The house placements highlight the areas of life where the partnership's energies are most likely to manifest. For example, a composite Sun in the 7th house might suggest that the relationship's focus is on partnership and collaboration.
+
+Just as in individual birth charts, the planets in the composite chart embody distinct archetypes and energies. The composite Sun signifies the essence and purpose of the relationship, while the Moon reflects its emotional undercurrents. Mercury governs communication and intellectual exchange, Venus represents love and harmony, Mars embodies passion and action, and so forth. By delving into the planetary placements, an astrologer can decode the intricate cosmic dance occurring within the partnership.
+
+The composite chart doesn't remain static; it evolves over time just as relationships do. The transits and progressions to the composite chart provide valuable insights into the ebb and flow of energies within the partnership. Transits can spark significant events, while progressions illuminate the long-term evolution of the relationship's dynamics. Observing these celestial movements allows astrologers to offer guidance and insight during pivotal moments in the partnership's journey.
+
+Every relationship has its challenges, and the composite chart is no exception. Challenging aspects in the composite chart can indicate areas of friction, miscommunication, or differing needs. However, these challenges also hold the potential for growth and transformation. Through understanding the cosmic dynamics at play, partners can work together to navigate these challenges and harness the potential for mutual evolution.
+
+It is through the wisdom of the composite chart that we uncover the hidden cosmic bonds that unite two souls on their shared journey through life's enigmatic cosmos.
+
+## Davison Chart: mid-points in space and time
+
+In the world of astrology, the Davison Chart is a hidden gem, a celestial tapestry that weaves the energies of two individuals into a single, unique chart. Named after its creator Ronald C. Davison, this chart holds the secrets of a relationship's dynamics, potentials, and challenges.
+
+The Davison Chart was devised as a response to this question. It's constructed by finding the midpoint between two individuals' birth dates, times, and places. This midpoint becomes the birth moment for the relationship itself, creating a chart that is representative of the unique energy shared between two people.
+
+At the heart of the Davison Chart lies the idea that a relationship is a separate entity, with its own character and destiny. Just as two musical notes create a new harmony when played together, the energies of two individuals combine to form a new cosmic melody in the Davison Chart. Planets, houses, and aspects in this chart reflect the qualities, challenges, and potentials that emerge from this cosmic union.
+
+In the Davison Chart, the positions of planets take on profound significance. The conjunctions, oppositions, trines, and squares between the planets create a narrative of the relationship's dynamics. Harmonious aspects might signify shared goals, understanding, and ease of communication, while challenging aspects could point to areas of conflict and growth. For instance, a Venus-Mars trine might indicate a strong physical and emotional attraction, while a Sun-Saturn square could point to tests and lessons in the relationship.
+
+The houses in the Davison Chart reveal where the energies of the relationship are focused. The placements of planets within these houses offer insights into the areas of life that will be most affected by the relationship's presence. The 7th house, representing partnerships, gains even more significance in this chart. Planets located here shed light on the qualities that the relationship brings out in each person and how the partnership impacts their individual lives.
+
+## Coalescent Chart: harmonics and events
+
+The Coalescent Chart, developed by Lawrence Grinnell, is a technique for analyzing astrological relationships and very lesser known. I've been trying to learn it myself and haven't been able to find out much about the technique. This 20th century method involves assigning harmonics to planets and operates akin to a composite chart. By examining the relative positions of celestial bodies, the Moon's Nodes, and the Midheaven in a pair of natal charts, Coalescent Charts provide insights into transit sensitivity, shedding light on the dynamics between two individuals. To determine the harmonic, one calculates the shortest arcs in both birth charts and divides the angles into 360. The planet's position is then multiplied by the harmonic figure to initiate the assessment of compatibility. For more detailed information, you can refer to [this source](http://www.gotohoroscope.com/astrology-dictionary/c-3.html).
+
+These charts are utilized to highlight an exceptional responsiveness to transits, making them valuable tools for fostering connections between individuals or for understanding the connection between a person and a significant event. I don't use this technique in sessions, but appreciate its modernity.
+
+In the end, all of the techniques above serve as a reminder that, just as the stars influence us, we also have the power to shape our interactions and relationships. It's a blend of fate and free will, offering a celestial roadmap to navigate the ever-changing landscape of human connections. So, whether you're exploring a new romance, nurturing a friendship, or collaborating with colleagues, astrology can provide a guiding light to help you navigate the cosmic dance of relationships.
+
+---
+
+## Synastry in Depth: Key Interchart Aspects
+
+Synastry is the comparison of two natal charts through **interchart aspects** — how Person A's planets aspect Person B's planets. Below are the most significant interchart aspects for relationship interpretation.
+
+### How to Read Interchart Aspects
+
+- Each person **plays the role** of their planet in the aspect. If A's Saturn conjuncts B's Sun, A takes on the Saturn role when B expresses Sun qualities.
+- **Sign and house placements** modify the expression. A Sun-Saturn conjunction in Scorpio plays out very differently than in Sagittarius.
+- Prioritize **inner planet interaspects** (Sun through Saturn) and aspects involving **relationship significators** (7th house rulers, Venus, Mars).
+- **Orbs**: Keep to 5° for personal planets, 3° for outer planets. Tighter orbs = stronger effect.
+
+### Sun Interchart Aspects
+
+| Aspect | Meaning |
+|---|---|
+| **Sun conjunct Sun** | Strong sense of shared identity; you "get" each other's core nature. Can be competitive in hard aspects. |
+| **Sun conjunct Moon** | Deep emotional understanding; natural nurturing. One of the best aspects for long-term compatibility. |
+| **Sun conjunct Venus** | Warmth, affection, mutual admiration. Easy romantic connection. |
+| **Sun conjunct Mars** | Strong attraction and energy. Can be competitive or combative in square/opposition. |
+| **Sun conjunct Saturn** | Serious, fated feeling. Saturn person can feel restrictive; Sun person may feel "held back." Enduring if worked through. |
+| **Sun conjunct North Node** | Fated connection. Sun person helps Node person grow toward their life direction. |
+
+### Moon Interchart Aspects
+
+| Aspect | Meaning |
+|---|---|
+| **Moon conjunct Moon** | Emotional compatibility; you feel similarly about security, home, and comfort. |
+| **Moon conjunct Venus** | Tenderness, caring, domestic happiness. Classic for comfortable cohabitation. |
+| **Moon conjunct Mars** | Emotional stimulation but potential for fights. One person's actions trigger the other's emotions. |
+| **Moon conjunct Saturn** | Deep emotional bonds but possible withdrawal, criticism, or fear of vulnerability. Karmic undertone. |
+| **Moon conjunct North Node** | Past-life or soul-contract feeling. Significant emotional role in each other's path. |
+
+### Venus Interchart Aspects
+
+| Aspect | Meaning |
+|---|---|
+| **Venus conjunct Venus** | Shared values in love, aesthetics, and pleasure. Natural romantic compatibility. |
+| **Venus conjunct Mars** | Classic chemistry/sexual attraction. Conjunction = intense passion; trine/sextile = easy flow; square = friction and push-pull. |
+| **Venus conjunct Saturn** | Serious, fated love. Loyalty and commitment, but can feel heavy or like duty. Karmic undertone. |
+| **Venus conjunct North Node** | Fated romantic connection. Venus person embodies qualities the Node person needs to develop. |
+
+### Mars Interchart Aspects
+
+| Aspect | Meaning |
+|---|---|
+| **Mars conjunct Mars** | Shared drive and energy. Can be competitive or combative in hard aspects. |
+| **Mars conjunct Saturn** | Frustrated desire, blocked anger, or "walking on eggshells." Can also indicate disciplined action together. |
+| **Mars conjunct Pluto** | Powerful, obsessive sexual intensity. Can be controlling; requires maturity. |
+
+### Saturn Interchart Aspects
+
+Saturn in synastry shows where the relationship feels **binding, serious, and sometimes heavy**.
+
+| Aspect | Meaning |
+|---|---|
+| **Saturn conjunct Sun** | Fated, dutiful. Saturn person acts as teacher/taskmaster. Can feel restrictive. |
+| **Saturn conjunct Moon** | Deep emotional bonds with possible emotional withdrawal or fear of vulnerability. |
+| **Saturn conjunct Venus** | Long-term potential with lessons in self-worth and love. Can feel like duty. |
+| **Saturn conjunct North Node** | Karmic contract. Relationship pushes Node person toward growth. Serious lifetime lesson. |
+| **Saturn square Nodes** | Karmic block or tension. Timing or life paths hard to synchronize. Forces confrontation with unfinished lessons. |
+
+### Node Interchart Aspects
+
+The lunar nodes in synastry indicate **karmic themes, soul contracts, and the feeling of "we were meant to meet."**
+
+- **Personal planets on the North Node**: Destined connection. The Node person feels pulled toward the planet person's qualities for growth.
+- **Personal planets on the South Node**: "I've known you forever" feeling. Past-life bond — familiar but sometimes hard to move forward from.
+- **Saturn-Node aspects**: Intensify the karmic contract feeling and bring serious lessons.
+
+### House Overlays in Synastry
+
+When one person's planets fall in another person's houses, they activate that life area:
+
+| House | Activation |
+|---|---|
+| **1st** | The planet person is strongly visible to the house person; shapes their self-image. |
+| **4th** | Deep emotional significance; feeling "at home" with each other. |
+| **7th** | Strong partnership significance; the planet person embodies partnership energy. |
+| **8th** | Intense psychological or sexual bond; shared resources and transformation. |
+| **10th** | Public significance; the planet person affects the house person's career/reputation. |
+| **12th** | Hidden or spiritual connection; can feel fated but also confusing. |
+
+---
+
+## Composite Chart in Depth: Practical Reading Guide
+
+The composite chart describes the **relationship as its own entity** — not what either person wants, but what the relationship "wants."
+
+### Key Principle
+
+> "The composite Sun is not about what either person wants; it is about what the relationship wants." — Augurine
+
+Everything in this chart describes the **merged entity** that two people create. Composite Moon in Scorpio doesn't mean either person is emotionally intense — it means the *relationship entity* has that quality.
+
+### Reading Order
+
+1. **Chart shape**: Which houses contain clusters? Upper half = public-facing; lower half = private/internal.
+2. **Composite Ascendant**: How the relationship initiates things and how outsiders perceive the couple.
+3. **Composite Sun**: The relationship's identity and purpose (apparent once past early stages).
+4. **Composite Moon**: The emotional pipeline — how comfort and safety flow (or fail to flow).
+5. **Angular planets** (conjunct AC, MC, DC, IC): The loudest voices in the relationship.
+6. **Most-aspected planet**: The theme the relationship keeps returning to.
+7. **Unaspected planets**: Relationship blind spots.
+8. **Chart ruler** (planet ruling the composite Ascendant sign): How the relationship navigates challenges.
+
+### Composite Planet Meanings
+
+| Planet | Composite Meaning |
+|---|---|
+| **Sun** | Core identity and purpose of the relationship |
+| **Moon** | Emotional tone, comfort, daily rhythms |
+| **Mercury** | Communication style, how the couple thinks together |
+| **Venus** | Love style, affection, shared values, harmony |
+| **Mars** | Drive, passion, conflict style, sexual energy |
+| **Jupiter** | Growth, optimism, shared beliefs, expansion |
+| **Saturn** | Structure, commitment, limitations, tests |
+| **Uranus** | Unpredictability, freedom, innovation, disruption |
+| **Neptune** | Idealism, spiritual connection, illusion, dissolution |
+| **Pluto** | Power dynamics, transformation, obsession, depth |
+
+### Composite House Emphasis
+
+| House | Relationship Focus |
+|---|---|
+| **1st** | Identity as a couple; how you present yourselves |
+| **4th** | Home, family, private life, emotional foundation |
+| **5th** | Creativity, romance, children, fun together |
+| **6th** | Daily routines, service, health (challenging for romance) |
+| **7th** | Partnership itself; how you relate to others as a unit |
+| **8th** | Shared resources, intimacy, psychological depth |
+| **9th** | Shared beliefs, travel, higher learning, adventure |
+| **10th** | Career, public role, shared ambition |
+| **11th** | Friendship, community, shared ideals |
+| **12th** | Spiritual connection, hidden dynamics (challenging) |
+
+### Composite Aspects
+
+- **Orbs**: Keep to **3° maximum** (tighter than natal). Sub-1° aspects carry the most weight.
+- **Saturn aspects**: Structural limitations. No Saturn aspects = free but unstable. Strong Saturn = endurance but demands maintenance.
+- **Pluto aspects**: Transformation and power struggles.
+- **Neptune aspects**: Idealization that can turn to disillusionment. Handle with care.
+- **Sun-Satron conjunction**: Defining feature — serious, heavy, but potentially very enduring.
+
+### Common Mistakes
+
+1. **Reading composite like a natal chart** for one person. Nothing here belongs to either individual.
+2. **Treating difficult aspects as doom**. Sun square Saturn describes a serious-feeling relationship, not a doomed one. Many long-lasting marriages have this.
+3. **Reading in isolation**. Always use synastry + natal charts alongside the composite.
+
+---
+
+## Davison Chart in Depth: The Relationship's "Real" Birth Chart
+
+### Technical Construction
+
+The Davison chart is built by **averaging the two birth dates, times, and places** to find a single midpoint in time and space, then casting a chart for that exact moment and location.
+
+Unlike the composite (which is purely conceptual), the Davison chart is a **"real" chart** — it corresponds to an actual moment in time and place. Someone could have been born at that exact moment.
+
+### Composite vs. Davison: When to Use Which
+
+| | **Composite** | **Davison** |
+|---|---|---|
+| **Nature** | Symbolic/conceptual | Real moment in time/space |
+| **Shows** | Structure, dynamics, public face | Felt experience, inner emotional tone |
+| **Best for** | How the relationship functions and appears | How the relationship feels from the inside |
+| **Timing** | Transits only | Transits + progressions + solar arcs |
+| **Analogy** | The relationship's "blueprint" | The relationship's "natal chart" |
+
+### Practical Staging
+
+Some astrologers stage their use:
+- **Synastry** → when people first meet
+- **Composite** → once the relationship starts
+- **Davison** → once formally committed/engaged (deeper, long-term chart)
+
+### When They Differ
+
+It's common for composite and Davison to show different charts with overlapping themes. Look for **repeated patterns** across both — same planets emphasized, similar house themes. They are often "two versions of the same story."
+
+### The Full Relationship Reading
+
+For serious relationship analysis, use all three layers:
+
+1. **Natal charts** — individual context and needs
+2. **Synastry** — interaction patterns, chemistry, friction
+3. **Composite** — the relationship's structure and public face
+4. **Davison** — the relationship's inner experience and evolution over time
+
+---
+
+## Coalescent Chart: Harmonic Responsiveness
+
+The **Coalescent Chart**, developed by Lawrence Grinnell, is the least known of the four techniques. It operates on **harmonic principles**:
+
+- Calculates the **shortest arcs** between planets in both birth charts
+- Divides 360° by these arcs to determine the **harmonic number**
+- Multiplies each planet's position by the harmonic figure
+- The resulting chart highlights **transit sensitivity** — exceptional responsiveness to external triggers
+
+**Use**: Understanding connections between a person and a significant event, or identifying relationships with unusual sensitivity to transits.
+
+---
+
+## Relationship Types and Chart Emphasis
+
+While all techniques apply to any relationship type, certain chart features carry more weight depending on the relationship:
+
+### Romantic / Sexual Relationships
+- **Venus-Mars aspects** (synastry and composite) for chemistry
+- **5th house** (romance, pleasure) and **8th house** (intimacy, sexuality) emphasis
+- **Moon** contacts for emotional bonding
+- **Saturn** contacts for commitment and longevity
+- **Pluto** contacts for transformative intensity
+
+### Business / Professional Partnerships
+- **Mercury** contacts for communication and planning
+- **6th house** (daily work) and **10th house** (career, public role) emphasis
+- **Saturn** contacts for structure and reliability
+- **Jupiter** contacts for growth and opportunity
+- **Uranus** contacts for innovation
+
+### Family Dynamics
+- **Moon** contacts for emotional patterns
+- **4th house** (home, family roots) emphasis
+- **Saturn** contacts for duty, obligation, parental roles
+- **Node** contacts for karmic family patterns
+
+### Friendships
+- **Mercury** contacts for intellectual connection
+- **11th house** (friendships, groups) emphasis
+- **Jupiter** contacts for shared optimism and growth
+- **Uranus** contacts for unconventional bonds
+
+---
+
+## Sources
+
+- WildWitchWest — *Relationship Astrology Techniques: Synastry, Composite, Davison, and Coalescent Charts*
+  https://www.wildwitchwest.com/single-post/relationship-astrology-techniques-synastry-composite-davison-and-coalescent-charts
+- Cafe Astrology — *Interchart Aspects (Synastry)*
+  https://cafeastrology.com/synastry/interchartaspects.html
+- Augurine — *How to Read a Composite Chart*
+  https://www.augurine.com/learn/how-to-read-composite-chart
+- ElsaElsa — *Davison vs. Composite Charts*
+  https://elsaelsa.com/astrology/davison-vs-composite-charts/
+- Perplexity AI — *Synastry Aspects: Venus, Mars, Saturn, Moon, Nodes*
+- Perplexity AI — *Composite vs. Davison Chart Differences*

+ 922 - 0
src/astro_mcp/astrology.py

@@ -73,6 +73,7 @@ ASPECT_DEFINITIONS: list[dict[str, Any]] = [
     {"name": "sextile", "angle": 60.0, "default_orb": 6.0, "symbol": "Sx"},
     {"name": "square", "angle": 90.0, "default_orb": 8.0, "symbol": "Sq"},
     {"name": "trine", "angle": 120.0, "default_orb": 8.0, "symbol": "Tr"},
+    {"name": "quincunx", "angle": 150.0, "default_orb": 3.0, "symbol": "Qx"},
     {"name": "opposition", "angle": 180.0, "default_orb": 8.0, "symbol": "Op"},
 ]
 
@@ -649,3 +650,924 @@ def _midpoint(lon1: float, lon2: float) -> float:
         return normalize_degrees((lon1 + lon2) / 2.0)
     else:
         return normalize_degrees((lon1 + lon2) / 2.0 + 180.0)
+
+
+# ── Sign Ruler Lookup ────────────────────────────────────────────────
+
+# Modern rulership (Placidus/Modern Western)
+SIGN_RULERS: dict[str, str] = {
+    "Aries": "mars",
+    "Taurus": "venus",
+    "Gemini": "mercury",
+    "Cancer": "moon",
+    "Leo": "sun",
+    "Virgo": "mercury",
+    "Libra": "venus",
+    "Scorpius": "pluto",
+    "Sagittarius": "jupiter",
+    "Capricornus": "saturn",
+    "Aquarius": "uranus",
+    "Pisces": "neptune",
+}
+
+# Traditional rulership (pre-discovery of outer planets)
+SIGN_RULERS_TRADITIONAL: dict[str, str] = {
+    "Aries": "mars",
+    "Taurus": "venus",
+    "Gemini": "mercury",
+    "Cancer": "moon",
+    "Leo": "sun",
+    "Virgo": "mercury",
+    "Libra": "venus",
+    "Scorpius": "mars",
+    "Sagittarius": "jupiter",
+    "Capricornus": "saturn",
+    "Aquarius": "saturn",
+    "Pisces": "jupiter",
+}
+
+# Element lookup by sign
+SIGN_ELEMENTS: dict[str, str] = {
+    "Aries": "fire", "Taurus": "earth", "Gemini": "air", "Cancer": "water",
+    "Leo": "fire", "Virgo": "earth", "Libra": "air", "Scorpius": "water",
+    "Sagittarius": "fire", "Capricornus": "earth", "Aquarius": "air", "Pisces": "water",
+}
+
+# Modality lookup by sign
+SIGN_MODALITIES: dict[str, str] = {
+    "Aries": "cardinal", "Taurus": "fixed", "Gemini": "mutable", "Cancer": "cardinal",
+    "Leo": "fixed", "Virgo": "mutable", "Libra": "cardinal", "Scorpius": "fixed",
+    "Sagittarius": "mutable", "Capricornus": "cardinal", "Aquarius": "fixed", "Pisces": "mutable",
+}
+
+# Element to signs
+ELEMENT_SIGNS: dict[str, list[str]] = {
+    "fire": ["Aries", "Leo", "Sagittarius"],
+    "earth": ["Taurus", "Virgo", "Capricornus"],
+    "air": ["Gemini", "Libra", "Aquarius"],
+    "water": ["Cancer", "Scorpius", "Pisces"],
+}
+
+# Modality to signs
+MODALITY_SIGNS: dict[str, list[str]] = {
+    "cardinal": ["Aries", "Cancer", "Libra", "Capricornus"],
+    "fixed": ["Taurus", "Leo", "Scorpius", "Aquarius"],
+    "mutable": ["Gemini", "Virgo", "Sagittarius", "Pisces"],
+}
+
+# Angular houses
+ANGULAR_HOUSES = {1, 4, 7, 10}
+SUCCEDENT_HOUSES = {2, 5, 8, 11}
+CADENT_HOUSES = {3, 6, 9, 12}
+
+# Personal planets (for karmic filtering)
+PERSONAL_PLANETS = {"sun", "moon", "mercury", "venus", "mars"}
+
+# Hard aspects (for karmic filtering)
+HARD_ASPECTS = {"conjunction", "square", "opposition"}
+
+
+def _planet_sign(planet_name: str, planets: list[dict[str, Any]]) -> str | None:
+    """Look up the sign of a planet from a planet list."""
+    for p in planets:
+        if p["body"] == planet_name:
+            return p.get("sign")
+    return None
+
+
+def _planet_house(planet_name: str, planets: list[dict[str, Any]]) -> int | None:
+    """Look up the house of a planet from a planet list."""
+    for p in planets:
+        if p["body"] == planet_name:
+            return p.get("house")
+    return None
+
+
+def _planet_lon(planet_name: str, planets: list[dict[str, Any]]) -> float | None:
+    """Look up the absolute_lon of a planet from a planet list."""
+    for p in planets:
+        if p["body"] == planet_name:
+            return p.get("absolute_lon")
+    return None
+
+
+# ── Chart Overview Functions ──────────────────────────────────────────
+
+
+def get_element_balance(planets: list[dict[str, Any]]) -> dict[str, Any]:
+    """Count planets by element (fire/earth/air/water).
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        Dict with counts and percentages per element.
+    """
+    counts: dict[str, int] = {"fire": 0, "earth": 0, "air": 0, "water": 0}
+    for p in planets:
+        sign = p.get("sign", "")
+        element = SIGN_ELEMENTS.get(sign)
+        if element:
+            counts[element] += 1
+    total = sum(counts.values())
+    percentages = {k: round(v / total * 100, 1) if total > 0 else 0.0 for k, v in counts.items()}
+    return {"counts": counts, "percentages": percentages, "total": total}
+
+
+def get_modality_balance(planets: list[dict[str, Any]]) -> dict[str, Any]:
+    """Count planets by modality (cardinal/fixed/mutable).
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        Dict with counts and percentages per modality.
+    """
+    counts: dict[str, int] = {"cardinal": 0, "fixed": 0, "mutable": 0}
+    for p in planets:
+        sign = p.get("sign", "")
+        modality = SIGN_MODALITIES.get(sign)
+        if modality:
+            counts[modality] += 1
+    total = sum(counts.values())
+    percentages = {k: round(v / total * 100, 1) if total > 0 else 0.0 for k, v in counts.items()}
+    return {"counts": counts, "percentages": percentages, "total": total}
+
+
+def get_hemisphere_emphasis(planets: list[dict[str, Any]]) -> dict[str, int]:
+    """Count planets by hemisphere (upper/lower/east/west).
+
+    Upper = houses 7-12, Lower = houses 1-6.
+    East = houses 10-3 (via 12/1/2), West = houses 4-9.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        Dict with counts per hemisphere.
+    """
+    upper = 0
+    lower = 0
+    east = 0
+    west = 0
+    for p in planets:
+        house = p.get("house", 1)
+        if 7 <= house <= 12:
+            upper += 1
+        else:
+            lower += 1
+        if house in (10, 11, 12, 1, 2, 3):
+            east += 1
+        else:
+            west += 1
+    return {"upper": upper, "lower": lower, "east": east, "west": west}
+
+
+def detect_stelliums(planets: list[dict[str, Any]]) -> list[dict[str, Any]]:
+    """Detect stelliums: 3+ planets in the same sign or house.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        List of stellium dicts with type, key, and involved planets.
+    """
+    result = []
+
+    # By sign
+    sign_groups: dict[str, list[str]] = {}
+    for p in planets:
+        sign = p.get("sign", "")
+        if sign not in sign_groups:
+            sign_groups[sign] = []
+        sign_groups[sign].append(p["body"])
+    for sign, bodies in sign_groups.items():
+        if len(bodies) >= 3:
+            result.append({"type": "sign", "key": sign, "planets": bodies})
+
+    # By house
+    house_groups: dict[int, list[str]] = {}
+    for p in planets:
+        house = p.get("house", 1)
+        if house not in house_groups:
+            house_groups[house] = []
+        house_groups[house].append(p["body"])
+    for house, bodies in house_groups.items():
+        if len(bodies) >= 3:
+            result.append({"type": "house", "key": str(house), "planets": bodies})
+
+    return result
+
+
+def get_empty_houses(planets: list[dict[str, Any]]) -> list[int]:
+    """Return house numbers (1-12) that have no planets.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        Sorted list of empty house numbers.
+    """
+    occupied = {p.get("house", 1) for p in planets}
+    return sorted(h for h in range(1, 13) if h not in occupied)
+
+
+def get_chart_ruler(
+    ascendant_sign: str,
+    planets: list[dict[str, Any]],
+    traditional: bool = False,
+) -> dict[str, Any] | None:
+    """Identify the chart ruler: the planet ruling the Ascendant sign.
+
+    Args:
+        ascendant_sign: Sign on the Ascendant (e.g., "Aries").
+        planets: Planet list from calculate_natal_chart output.
+        traditional: If True, use traditional rulership (no outer planets).
+
+    Returns:
+        Dict with ruler name, sign, house, retrograde, and absolute_lon.
+        None if the ruler is not found in the planet list.
+    """
+    rulers = SIGN_RULERS_TRADITIONAL if traditional else SIGN_RULERS
+    ruler_name = rulers.get(ascendant_sign)
+    if not ruler_name:
+        return None
+    for p in planets:
+        if p["body"] == ruler_name:
+            return {
+                "body": p["body"],
+                "sign": p.get("sign"),
+                "house": p.get("house"),
+                "retrograde": p.get("retrograde", False),
+                "absolute_lon": p.get("absolute_lon"),
+            }
+    return None
+
+
+def get_house_rulers(
+    houses: list[dict[str, Any]],
+    planets: list[dict[str, Any]],
+    traditional: bool = False,
+) -> list[dict[str, Any]]:
+    """For each house, find the ruling planet and its condition.
+
+    Args:
+        houses: House list from calculate_natal_chart output.
+        planets: Planet list from calculate_natal_chart output.
+        traditional: If True, use traditional rulership.
+
+    Returns:
+        List of dicts with house, cusp_sign, ruler, ruler_sign, ruler_house, ruler_retrograde.
+    """
+    rulers = SIGN_RULERS_TRADITIONAL if traditional else SIGN_RULERS
+    result = []
+    for h in houses:
+        cusp_sign = h.get("sign", "")
+        ruler_name = rulers.get(cusp_sign, "")
+        ruler_info = {
+            "house": h["house"],
+            "cusp_sign": cusp_sign,
+            "ruler": ruler_name,
+            "ruler_sign": None,
+            "ruler_house": None,
+            "ruler_retrograde": None,
+        }
+        if ruler_name:
+            for p in planets:
+                if p["body"] == ruler_name:
+                    ruler_info["ruler_sign"] = p.get("sign")
+                    ruler_info["ruler_house"] = p.get("house")
+                    ruler_info["ruler_retrograde"] = p.get("retrograde", False)
+                    break
+        result.append(ruler_info)
+    return result
+
+
+def group_planets_by_house(planets: list[dict[str, Any]]) -> dict[int, list[str]]:
+    """Group planet names by house number.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        Dict mapping house number (1-12) to list of planet names.
+    """
+    result: dict[int, list[str]] = {}
+    for p in planets:
+        house = p.get("house", 1)
+        if house not in result:
+            result[house] = []
+        result[house].append(p["body"])
+    return result
+
+
+def group_planets_by_sign(planets: list[dict[str, Any]]) -> dict[str, list[str]]:
+    """Group planet names by sign.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        Dict mapping sign name to list of planet names.
+    """
+    result: dict[str, list[str]] = {}
+    for p in planets:
+        sign = p.get("sign", "")
+        if sign not in result:
+            result[sign] = []
+        result[sign].append(p["body"])
+    return result
+
+
+def get_house_type_counts(planets: list[dict[str, Any]]) -> dict[str, int]:
+    """Count planets by house type: angular, succedent, cadent.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        Dict with counts for each house type.
+    """
+    counts = {"angular": 0, "succedent": 0, "cadent": 0}
+    for p in planets:
+        house = p.get("house", 1)
+        if house in ANGULAR_HOUSES:
+            counts["angular"] += 1
+        elif house in SUCCEDENT_HOUSES:
+            counts["succedent"] += 1
+        elif house in CADENT_HOUSES:
+            counts["cadent"] += 1
+    return counts
+
+
+def get_retrograde_planets(planets: list[dict[str, Any]]) -> list[dict[str, Any]]:
+    """Return all retrograde planets with their sign and house.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        List of retrograde planet dicts with body, sign, house.
+    """
+    return [
+        {"body": p["body"], "sign": p.get("sign"), "house": p.get("house")}
+        for p in planets
+        if p.get("retrograde", False)
+    ]
+
+
+# ── Karmic Helper Functions ───────────────────────────────────────────
+
+
+def get_nodal_axis(
+    planets: list[dict[str, Any]],
+    houses: list[dict[str, Any]] | None = None,
+) -> dict[str, Any]:
+    """Extract the nodal axis (North + South Node) from planet data.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+        houses: Optional house list for house placement.
+
+    Returns:
+        Dict with north_node and south_node, each having sign, house, degree, absolute_lon.
+    """
+    result: dict[str, Any] = {"north_node": None, "south_node": None}
+    for p in planets:
+        if p["body"] == "true_node":
+            north = {
+                "body": "true_node",
+                "sign": p.get("sign"),
+                "degree_within_sign": p.get("degree_within_sign"),
+                "absolute_lon": p.get("absolute_lon"),
+                "house": p.get("house"),
+            }
+            # South Node = opposite point
+            south_lon = normalize_degrees(p.get("absolute_lon", 0.0) + 180.0)
+            south_zodiac = ecliptic_to_zodiac(south_lon)
+            south = {
+                "body": "south_node",
+                "sign": south_zodiac["sign"],
+                "degree_within_sign": south_zodiac["degree"],
+                "absolute_lon": south_zodiac["absolute_lon"],
+            }
+            if houses:
+                south["house"] = get_house_placement(south_lon, houses)
+                north["house"] = p.get("house")
+            result["north_node"] = north
+            result["south_node"] = south
+            break
+    return result
+
+
+def get_saturn_info(planets: list[dict[str, Any]]) -> dict[str, Any] | None:
+    """Extract Saturn's position data from planet list.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+
+    Returns:
+        Dict with Saturn's body, sign, house, degree, retrograde, absolute_lon.
+        None if Saturn not found.
+    """
+    for p in planets:
+        if p["body"] == "saturn":
+            return {
+                "body": p["body"],
+                "sign": p.get("sign"),
+                "house": p.get("house"),
+                "degree_within_sign": p.get("degree_within_sign"),
+                "retrograde": p.get("retrograde", False),
+                "absolute_lon": p.get("absolute_lon"),
+            }
+    return None
+
+
+def get_pluto_polarity_point(
+    planets: list[dict[str, Any]],
+    houses: list[dict[str, Any]] | None = None,
+) -> dict[str, Any] | None:
+    """Calculate Pluto's Polarity Point (PPP) -- the point opposite Pluto.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+        houses: Optional house list for house placement.
+
+    Returns:
+        Dict with PPP sign, house, degree, absolute_lon. None if Pluto not found.
+    """
+    for p in planets:
+        if p["body"] == "pluto":
+            ppp_lon = normalize_degrees(p.get("absolute_lon", 0.0) + 180.0)
+            z = ecliptic_to_zodiac(ppp_lon)
+            result = {
+                "body": "pluto_polarity_point",
+                "sign": z["sign"],
+                "degree_within_sign": z["degree"],
+                "absolute_lon": z["absolute_lon"],
+            }
+            if houses:
+                result["house"] = get_house_placement(ppp_lon, houses)
+            return result
+    return None
+
+
+def get_part_of_fortune(
+    ascendant_lon: float,
+    sun_lon: float,
+    moon_lon: float,
+    houses: list[dict[str, Any]] | None = None,
+) -> dict[str, Any]:
+    """Calculate the Arabic Part of Fortune.
+
+    Formula: ASC + Moon - Sun (in ecliptic longitude).
+
+    Args:
+        ascendant_lon: Ascendant absolute longitude.
+        sun_lon: Sun absolute longitude.
+        moon_lon: Moon absolute longitude.
+        houses: Optional house list for house placement.
+
+    Returns:
+        Dict with sign, degree, absolute_lon, and optionally house.
+    """
+    pof_lon = normalize_degrees(ascendant_lon + moon_lon - sun_lon)
+    z = ecliptic_to_zodiac(pof_lon)
+    result = {
+        "body": "part_of_fortune",
+        "sign": z["sign"],
+        "degree_within_sign": z["degree"],
+        "absolute_lon": z["absolute_lon"],
+    }
+    if houses:
+        result["house"] = get_house_placement(pof_lon, houses)
+    return result
+
+
+def get_twelfth_house_analysis(
+    houses: list[dict[str, Any]],
+    planets: list[dict[str, Any]],
+    traditional: bool = False,
+) -> dict[str, Any]:
+    """Analyze the 12th house for karmic/spiritual themes.
+
+    Args:
+        houses: House list from calculate_natal_chart output.
+        planets: Planet list from calculate_natal_chart output.
+        traditional: If True, use traditional rulership for 12th house ruler.
+
+    Returns:
+        Dict with cusp_sign, planets_in_12th, ruler info.
+    """
+    cusp_sign = None
+    for h in houses:
+        if h["house"] == 12:
+            cusp_sign = h.get("sign")
+            break
+
+    planets_in_12th = [
+        {"body": p["body"], "sign": p.get("sign"), "degree_within_sign": p.get("degree_within_sign")}
+        for p in planets
+        if p.get("house") == 12
+    ]
+
+    ruler_info = None
+    if cusp_sign:
+        rulers = SIGN_RULERS_TRADITIONAL if traditional else SIGN_RULERS
+        ruler_name = rulers.get(cusp_sign)
+        if ruler_name:
+            for p in planets:
+                if p["body"] == ruler_name:
+                    ruler_info = {
+                        "ruler": ruler_name,
+                        "sign": p.get("sign"),
+                        "house": p.get("house"),
+                        "retrograde": p.get("retrograde", False),
+                    }
+                    break
+            if not ruler_info:
+                ruler_info = {"ruler": ruler_name, "sign": None, "house": None, "retrograde": None}
+
+    return {
+        "cusp_sign": cusp_sign,
+        "planets": planets_in_12th,
+        "ruler": ruler_info,
+    }
+
+
+def filter_aspects_by_planets(
+    aspects: list[dict[str, Any]],
+    planet_names: set[str],
+    aspect_types: set[str] | None = None,
+) -> list[dict[str, Any]]:
+    """Filter aspects to only those involving specific planets and optionally specific aspect types.
+
+    Args:
+        aspects: Aspect list from compute_aspects or calculate_natal_chart.
+        planet_names: Set of planet names to filter for (either body1 or body2).
+        aspect_types: Optional set of aspect names to filter for (e.g., {"conjunction", "square"}).
+
+    Returns:
+        Filtered list of aspects.
+    """
+    result = []
+    for asp in aspects:
+        b1 = asp.get("body1", "")
+        b2 = asp.get("body2", "")
+        # Strip transit_ and natal_ prefixes for matching
+        b1_clean = b1.replace("transit_", "").replace("natal_", "").replace("p1_", "").replace("p2_", "")
+        b2_clean = b2.replace("transit_", "").replace("natal_", "").replace("p1_", "").replace("p2_", "")
+        if b1_clean in planet_names or b2_clean in planet_names:
+            if aspect_types is None or asp.get("aspect") in aspect_types:
+                result.append(asp)
+    return result
+
+
+def get_natal_aspects_to_planets(
+    aspects: list[dict[str, Any]],
+    target_planets: set[str],
+    aspect_types: set[str] | None = None,
+) -> list[dict[str, Any]]:
+    """Get all natal aspects involving specific target planets.
+
+    Convenience wrapper around filter_aspects_by_planets for natal chart aspects.
+
+    Args:
+        aspects: Aspect list from calculate_natal_chart.
+        target_planets: Set of planet names (e.g., {"sun", "moon", "true_node"}).
+        aspect_types: Optional set of aspect types to filter for.
+
+    Returns:
+        Filtered aspects sorted by orb (tightest first).
+    """
+    filtered = filter_aspects_by_planets(aspects, target_planets, aspect_types)
+    filtered.sort(key=lambda a: a.get("orb", 999))
+    return filtered
+
+
+# ── Aspect Pattern Detection ─────────────────────────────────────────
+
+
+def detect_aspect_patterns(
+    planets: list[dict[str, Any]],
+    aspects: list[dict[str, Any]],
+    orb_limit: float = 8.0,
+) -> list[dict[str, Any]]:
+    """Detect major aspect patterns in a natal chart.
+
+    Scans the aspect list for T-squares, Grand Trines, Grand Crosses, and Yods.
+
+    Args:
+        planets: Planet list from calculate_natal_chart output.
+        aspects: Aspect list (formatted, with body1/body2 keys).
+        orb_limit: Maximum orb for pattern detection (default 8°).
+
+    Returns:
+        List of pattern dicts with type, planets, apex, element, modality.
+    """
+    patterns: list[dict[str, Any]] = []
+
+    # Build a set of aspect pairs for fast lookup
+    # Key: (body_a, body_b, aspect_type) where body_a < body_b alphabetically
+    aspect_set: dict[tuple[str, str, str], dict[str, Any]] = {}
+    for asp in aspects:
+        b1 = asp.get("body1", "")
+        b2 = asp.get("body2", "")
+        asp_type = asp.get("aspect", "")
+        orb = asp.get("orb", 999)
+        if orb > orb_limit:
+            continue
+        key = (min(b1, b2), max(b1, b2), asp_type)
+        aspect_set[key] = asp
+
+    def _has_aspect(b1: str, b2: str, asp_type: str) -> bool:
+        return (min(b1, b2), max(b1, b2), asp_type) in aspect_set
+
+    def _get_orb(b1: str, b2: str, asp_type: str) -> float:
+        key = (min(b1, b2), max(b1, b2), asp_type)
+        return aspect_set[key].get("orb", 999) if key in aspect_set else 999
+
+    planet_names = [p["body"] for p in planets]
+
+    # ── T-square: two planets in opposition, both squaring a third ──
+    for asp in aspects:
+        if asp.get("aspect") != "opposition" or asp.get("orb", 999) > orb_limit:
+            continue
+        a = asp["body1"]
+        b = asp["body2"]
+        for c in planet_names:
+            if c == a or c == b:
+                continue
+            if _has_aspect(a, c, "square") and _has_aspect(b, c, "square"):
+                orb_ac = _get_orb(a, c, "square")
+                orb_bc = _get_orb(b, c, "square")
+                if orb_ac <= orb_limit and orb_bc <= orb_limit:
+                    # Determine modality from the signs
+                    signs = []
+                    for pname in [a, b, c]:
+                        for p in planets:
+                            if p["body"] == pname:
+                                signs.append(p.get("sign", ""))
+                                break
+                    modality = None
+                    if len(signs) == 3:
+                        mods = [SIGN_MODALITIES.get(s) for s in signs]
+                        if len(set(mods)) == 1 and mods[0]:
+                            modality = mods[0]
+                    patterns.append({
+                        "type": "T-square",
+                        "planets": sorted([a, b, c]),
+                        "apex": c,
+                        "apex_house": _planet_house(c, planets),
+                        "houses": sorted([_planet_house(a, planets) or 0, _planet_house(b, planets) or 0, _planet_house(c, planets) or 0]),
+                        "signs": signs,
+                        "modality": modality,
+                        "orb_max": max(asp.get("orb", 0), orb_ac, orb_bc),
+                    })
+
+    # ── Grand Trine: three planets all in trine (same element) ──
+    trine_pairs: list[tuple[str, str, float]] = []
+    for asp in aspects:
+        if asp.get("aspect") == "trine" and asp.get("orb", 999) <= orb_limit:
+            trine_pairs.append((asp["body1"], asp["body2"], asp.get("orb", 999)))
+
+    # Find triangles in trine pairs
+    trine_graph: dict[str, list[str]] = {}
+    for b1, b2, _ in trine_pairs:
+        if b1 not in trine_graph:
+            trine_graph[b1] = []
+        if b2 not in trine_graph:
+            trine_graph[b2] = []
+        trine_graph[b1].append(b2)
+        trine_graph[b2].append(b1)
+
+    found_trines: set[frozenset] = set()
+    for a in trine_graph:
+        for b in trine_graph.get(a, []):
+            for c in trine_graph.get(b, []):
+                if c != a and a in trine_graph.get(c, []):
+                    trio = frozenset([a, b, c])
+                    if trio not in found_trines:
+                        found_trines.add(trio)
+                        # Determine element
+                        signs = []
+                        for pname in [a, b, c]:
+                            for p in planets:
+                                if p["body"] == pname:
+                                    signs.append(p.get("sign", ""))
+                                    break
+                        elements = [SIGN_ELEMENTS.get(s) for s in signs]
+                        element = elements[0] if elements and len(set(elements)) == 1 else None
+                        max_orb = max(
+                            _get_orb(a, b, "trine"),
+                            _get_orb(b, c, "trine"),
+                            _get_orb(a, c, "trine"),
+                        )
+                        patterns.append({
+                            "type": "Grand Trine",
+                            "planets": sorted([a, b, c]),
+                            "houses": sorted([_planet_house(a, planets) or 0, _planet_house(b, planets) or 0, _planet_house(c, planets) or 0]),
+                            "signs": signs,
+                            "element": element,
+                            "orb_max": max_orb,
+                        })
+
+    # ── Grand Cross: four planets forming 2 oppositions + 4 squares ──
+    opp_pairs = [(asp["body1"], asp["body2"]) for asp in aspects
+                 if asp.get("aspect") == "opposition" and asp.get("orb", 999) <= orb_limit]
+
+    found_crosses: set[frozenset] = set()
+    for i, (a, b) in enumerate(opp_pairs):
+        for c, d in opp_pairs[i + 1:]:
+            quartet = frozenset([a, b, c, d])
+            if quartet in found_crosses:
+                continue
+            # Check that each planet in pair 1 squares both planets in pair 2
+            if (_has_aspect(a, c, "square") and _has_aspect(a, d, "square") and
+                _has_aspect(b, c, "square") and _has_aspect(b, d, "square")):
+                found_crosses.add(quartet)
+                signs = []
+                for pname in sorted([a, b, c, d]):
+                    for p in planets:
+                        if p["body"] == pname:
+                            signs.append(p.get("sign", ""))
+                            break
+                mods = [SIGN_MODALITIES.get(s) for s in signs]
+                modality = mods[0] if len(set(mods)) == 1 and mods[0] else None
+                max_orb = max(
+                    _get_orb(a, b, "opposition"),
+                    _get_orb(c, d, "opposition"),
+                    _get_orb(a, c, "square"),
+                    _get_orb(a, d, "square"),
+                    _get_orb(b, c, "square"),
+                    _get_orb(b, d, "square"),
+                )
+                patterns.append({
+                    "type": "Grand Cross",
+                    "planets": sorted([a, b, c, d]),
+                    "houses": sorted([_planet_house(x, planets) or 0 for x in [a, b, c, d]]),
+                    "signs": signs,
+                    "modality": modality,
+                    "orb_max": max_orb,
+                })
+
+    # ── Yod: two planets in sextile, both quincunx a third ──
+    sextile_pairs = [(asp["body1"], asp["body2"]) for asp in aspects
+                     if asp.get("aspect") == "sextile" and asp.get("orb", 999) <= orb_limit]
+
+    found_yods: set[frozenset] = set()
+    for a, b in sextile_pairs:
+        for c in planet_names:
+            if c == a or c == b:
+                continue
+            if _has_aspect(a, c, "quincunx") and _has_aspect(b, c, "quincunx"):
+                trio = frozenset([a, b, c])
+                if trio not in found_yods:
+                    found_yods.add(trio)
+                    signs = []
+                    for pname in [a, b, c]:
+                        for p in planets:
+                            if p["body"] == pname:
+                                signs.append(p.get("sign", ""))
+                                break
+                    max_orb = max(
+                        _get_orb(a, b, "sextile"),
+                        _get_orb(a, c, "quincunx"),
+                        _get_orb(b, c, "quincunx"),
+                    )
+                    patterns.append({
+                        "type": "Yod",
+                        "planets": sorted([a, b, c]),
+                        "apex": c,
+                        "apex_house": _planet_house(c, planets),
+                        "houses": sorted([_planet_house(x, planets) or 0 for x in [a, b, c]]),
+                        "signs": signs,
+                        "orb_max": max_orb,
+                    })
+
+    return patterns
+
+
+# ── Chart Shape Detection ────────────────────────────────────────────
+
+
+def detect_chart_shape(planets: list[dict[str, Any]]) -> dict[str, Any]:
+    """Detect the chart shape pattern.
+
+    Analyzes the angular distribution of planets to classify the chart shape:
+    - Bundle: all planets within 120° arc
+    - Bowl: all planets within 180° arc
+    - Bucket: all planets within 250° arc with a singleton opposite
+    - Splash: planets distributed around the full chart
+    - Locomotive: planets within ~250° with a leading "locomotive" planet
+    - Seesaw: two opposing clusters
+    - Splay: three or more distributed pairs/groups
+
+    Args:
+        planets: Planet list from calculate_natal_chart output (must have absolute_lon).
+
+    Returns:
+        Dict with shape name, largest gap, and gap boundaries.
+    """
+    if len(planets) < 2:
+        return {"shape": "unknown", "largest_gap": 360.0, "gap_start": 0.0, "gap_end": 0.0}
+
+    lons = sorted([normalize_degrees(p.get("absolute_lon", 0.0)) for p in planets])
+    n = len(lons)
+
+    # Compute gaps between adjacent planets (including wraparound)
+    gaps: list[tuple[float, float, float]] = []  # (gap_size, start, end)
+    for i in range(n):
+        next_i = (i + 1) % n
+        gap = normalize_degrees(lons[next_i] - lons[i])
+        gaps.append((gap, lons[i], lons[next_i]))
+
+    # Find largest gap
+    largest_gap, gap_start, gap_end = max(gaps, key=lambda g: g[0])
+
+    # The occupied arc is 360 - largest_gap
+    occupied_arc = 360.0 - largest_gap
+
+    # Count planets in the largest gap
+    planets_in_gap = sum(1 for lon in lons if _is_in_arc(lon, gap_start, gap_end, largest_gap))
+    planets_in_occupied = n - planets_in_gap
+
+    # Classify
+    shape = "splash"  # default
+
+    if occupied_arc <= 120.0:
+        shape = "bundle"
+    elif occupied_arc <= 180.0:
+        shape = "bowl"
+    elif largest_gap < 90.0:
+        # No large empty arc -- planets are spread around the chart
+        shape = "splash"
+    elif occupied_arc <= 250.0:
+        # There's a significant empty arc (>= 90°) with all planets in the rest
+        if planets_in_gap == 0:
+            # All planets in occupied arc, clear empty zone
+            shape = "locomotive"
+        elif planets_in_gap == 1:
+            # Check if the singleton is truly isolated (not at the gap boundary)
+            gap_planet_lon = None
+            for lon in lons:
+                if _is_in_arc(lon, gap_start, gap_end, largest_gap):
+                    gap_planet_lon = lon
+                    break
+            # A true bucket singleton should not be exactly at gap_end
+            if gap_planet_lon is not None and abs(gap_planet_lon - gap_end) < 0.01:
+                # Planet is at the boundary -- check if gaps are even (splash)
+                second_largest = sorted(gaps, key=lambda g: g[0], reverse=True)[1][0] if len(gaps) > 1 else 0
+                if largest_gap - second_largest < 30:
+                    shape = "splash"
+                else:
+                    shape = "locomotive"
+            else:
+                shape = "bucket"
+        else:
+            # Multiple planets in the gap -- check if they form a pair
+            gap_planets = [lon for lon in lons if _is_in_arc(lon, gap_end, gap_start, largest_gap)]
+            if len(gap_planets) == 2:
+                gap_between_pair = abs(gap_planets[1] - gap_planets[0])
+                if gap_between_pair < 60:
+                    shape = "bucket"
+                else:
+                    shape = "seesaw"
+            else:
+                shape = "locomotive"
+    else:
+        # occupied_arc > 250, largest_gap < 110
+        # Check for seesaw: two clusters on opposite sides
+        second_largest = sorted(gaps, key=lambda g: g[0], reverse=True)[1] if len(gaps) > 1 else (0, 0, 0)
+        if second_largest[0] > 90:
+            shape = "seesaw"
+        else:
+            shape = "splay"
+
+    return {
+        "shape": shape,
+        "largest_gap": round(largest_gap, 2),
+        "gap_start": round(gap_start, 2),
+        "gap_end": round(gap_end, 2),
+        "occupied_arc": round(occupied_arc, 2),
+    }
+
+
+def _is_in_arc(lon: float, arc_start: float, arc_end: float, arc_size: float) -> bool:
+    """Check if a longitude falls strictly within an arc (going clockwise from start to end).
+
+    arc_start is exclusive, arc_end is inclusive. This avoids double-counting planets
+    that sit exactly on gap boundaries.
+    """
+    if arc_size >= 360.0:
+        return True
+    if arc_size <= 0.0:
+        return False
+    lon = normalize_degrees(lon)
+    arc_start = normalize_degrees(arc_start)
+    arc_end = normalize_degrees(arc_end)
+    if arc_start < arc_end:
+        return arc_start < lon <= arc_end
+    else:
+        # Wraps through 0
+        return lon > arc_start or lon <= arc_end

+ 81 - 4
src/astro_mcp/tools.py

@@ -113,6 +113,10 @@ async def calculate_natal_chart(
     elevation: float = 0.0,
     house_system: str = "placidus",
     orb_limits: dict[str, float] | None = None,
+    include_overview: bool = False,
+    include_patterns: bool = False,
+    include_karmic: bool = False,
+    top_n_aspects: int | None = None,
 ) -> dict[str, Any]:
     """Calculate a complete natal chart from birth data.
 
@@ -123,9 +127,17 @@ async def calculate_natal_chart(
         elevation: Birth elevation in meters.
         house_system: House system to use (default: Placidus).
         orb_limits: Optional dict of {aspect_name: max_orb_degrees}.
+        include_overview: If True, add element/modality/hemisphere balance,
+            stelliums, empty houses, chart ruler, house rulers, groupings.
+        include_patterns: If True, add aspect pattern detection (T-square,
+            Grand Trine, Grand Cross, Yod) and chart shape.
+        include_karmic: If True, add nodal axis, Saturn info, Pluto polarity point,
+            Part of Fortune, 12th house analysis, and karmic aspect filters.
+        top_n_aspects: If set, limit aspects to the N tightest by orb.
 
     Returns:
-        Complete natal chart structure with planets, houses, aspects, and angles.
+        Complete natal chart structure with planets, houses, aspects, angles,
+        and optionally overview, patterns, and karmic analysis.
     """
     sky = await call_sky_state(
         datetime=birth_datetime,
@@ -166,8 +178,12 @@ async def calculate_natal_chart(
             "retrograde": astrology.is_retrograde(speed_lon),
         })
 
-    # Calculate aspects
-    aspect_bodies = [{"name": p["body"], "lon": p["absolute_lon"]} for p in planets]
+    # Calculate aspects (pass speed_lon for applying/separating detection)
+    speed_lookup = {b["body"]: b.get("speed_lon") for b in raw_bodies}
+    aspect_bodies = [
+        {"name": p["body"], "lon": p["absolute_lon"], "speed_lon": speed_lookup.get(p["body"])}
+        for p in planets
+    ]
     aspects = astrology.compute_aspects(aspect_bodies, orb_limits)
 
     # Format aspects
@@ -185,7 +201,8 @@ async def calculate_natal_chart(
     # Calculate angles
     angles = astrology.calculate_angles(lst_hours, latitude)
 
-    return {
+    # Build result
+    result: dict[str, Any] = {
         "input": {
             "birth_datetime": birth_datetime,
             "latitude": latitude,
@@ -201,6 +218,66 @@ async def calculate_natal_chart(
         "angles": angles,
     }
 
+    # Add lunar phase from ephemeris
+    lunar_state = sky.get("lunar_state", {})
+    if lunar_state:
+        result["lunar_phase"] = {
+            "phase_name": lunar_state.get("phase_name"),
+            "illumination_fraction": lunar_state.get("illumination_fraction"),
+        }
+
+    # Limit aspects if requested
+    if top_n_aspects is not None:
+        result["aspects"] = formatted_aspects[:top_n_aspects]
+
+    # Overview section
+    if include_overview:
+        asc_sign = angles.get("ascendant", {}).get("sign", "")
+        overview: dict[str, Any] = {
+            "element_balance": astrology.get_element_balance(planets),
+            "modality_balance": astrology.get_modality_balance(planets),
+            "hemisphere_emphasis": astrology.get_hemisphere_emphasis(planets),
+            "stelliums": astrology.detect_stelliums(planets),
+            "empty_houses": astrology.get_empty_houses(planets),
+            "chart_ruler": astrology.get_chart_ruler(asc_sign, planets),
+            "house_rulers": astrology.get_house_rulers(houses, planets),
+            "planets_by_house": astrology.group_planets_by_house(planets),
+            "planets_by_sign": astrology.group_planets_by_sign(planets),
+            "house_type_counts": astrology.get_house_type_counts(planets),
+            "retrograde_planets": astrology.get_retrograde_planets(planets),
+        }
+        result["overview"] = overview
+
+    # Aspect patterns + chart shape
+    if include_patterns:
+        patterns = astrology.detect_aspect_patterns(planets, formatted_aspects)
+        chart_shape = astrology.detect_chart_shape(planets)
+        result["aspect_patterns"] = patterns
+        result["chart_shape"] = chart_shape
+
+    # Karmic analysis
+    if include_karmic:
+        asc_lon = angles.get("ascendant", {}).get("absolute_lon", 0.0)
+        sun_lon = astrology._planet_lon("sun", planets) or 0.0
+        moon_lon = astrology._planet_lon("moon", planets) or 0.0
+
+        karmic: dict[str, Any] = {
+            "nodal_axis": astrology.get_nodal_axis(planets, houses),
+            "saturn": astrology.get_saturn_info(planets),
+            "pluto_polarity_point": astrology.get_pluto_polarity_point(planets, houses),
+            "part_of_fortune": astrology.get_part_of_fortune(asc_lon, sun_lon, moon_lon, houses),
+            "twelfth_house": astrology.get_twelfth_house_analysis(houses, planets),
+            "nodal_aspects": astrology.get_natal_aspects_to_planets(
+                formatted_aspects, {"true_node"}
+            ),
+            "saturn_aspects": astrology.get_natal_aspects_to_planets(
+                formatted_aspects, {"saturn"}, astrology.HARD_ASPECTS
+            ),
+        }
+        result["karmic"] = karmic
+
+    return result
+
 
 # ── Tool: calculate_transit_chart ────────────────────────────────────
 

+ 718 - 0
tests/test_astrology.py

@@ -13,20 +13,54 @@ Tests cover:
 
 from __future__ import annotations
 
+from typing import Any
+
 import math
 
 import pytest
 
 from src.astro_mcp.astrology import (
+    ANGULAR_HOUSES,
+    CADENT_HOUSES,
     DEFAULT_ORBS,
+    ELEMENT_SIGNS,
+    HARD_ASPECTS,
+    MODALITY_SIGNS,
+    PERSONAL_PLANETS,
+    SIGN_ABBREVIATIONS,
+    SIGN_ELEMENTS,
+    SIGN_MODALITIES,
+    SIGN_RULERS,
+    SIGN_RULERS_TRADITIONAL,
+    SUCCEDENT_HOUSES,
     SUPPORTED_HOUSE_SYSTEMS,
     ZODIAC_SIGNS,
     calculate_angles,
     calculate_houses,
     compute_aspects,
     compute_composite_chart,
+    detect_aspect_patterns,
+    detect_chart_shape,
+    detect_stelliums,
     ecliptic_to_zodiac,
+    filter_aspects_by_planets,
+    get_chart_ruler,
+    get_element_balance,
+    get_empty_houses,
+    get_hemisphere_emphasis,
     get_house_placement,
+    get_house_rulers,
+    get_house_type_counts,
+    get_modality_balance,
+    get_nodal_axis,
+    get_natal_aspects_to_planets,
+    get_part_of_fortune,
+    get_pluto_polarity_point,
+    get_retrograde_planets,
+    get_saturn_info,
+    get_twelfth_house_analysis,
+    group_planets_by_house,
+    group_planets_by_sign,
     is_retrograde,
     normalize_degrees,
     zodiac_to_ecliptic,
@@ -441,3 +475,687 @@ class TestSupportedHouseSystems:
 
     def test_whole_sign_supported(self):
         assert "whole_sign" in SUPPORTED_HOUSE_SYSTEMS
+
+
+# ── Sign Ruler Lookup ────────────────────────────────────────────────
+
+class TestSignRulers:
+    def test_aries_ruler(self):
+        assert SIGN_RULERS["Aries"] == "mars"
+
+    def test_scorpius_ruler_modern(self):
+        assert SIGN_RULERS["Scorpius"] == "pluto"
+
+    def test_scorpius_ruler_traditional(self):
+        assert SIGN_RULERS_TRADITIONAL["Scorpius"] == "mars"
+
+    def test_aquarius_ruler_modern(self):
+        assert SIGN_RULERS["Aquarius"] == "uranus"
+
+    def test_aquarius_ruler_traditional(self):
+        assert SIGN_RULERS_TRADITIONAL["Aquarius"] == "saturn"
+
+    def test_all_signs_have_rulers(self):
+        for sign in ZODIAC_SIGNS:
+            assert sign in SIGN_RULERS
+            assert sign in SIGN_RULERS_TRADITIONAL
+
+
+class TestSignElements:
+    def test_fire_signs(self):
+        assert set(ELEMENT_SIGNS["fire"]) == {"Aries", "Leo", "Sagittarius"}
+
+    def test_earth_signs(self):
+        assert set(ELEMENT_SIGNS["earth"]) == {"Taurus", "Virgo", "Capricornus"}
+
+    def test_air_signs(self):
+        assert set(ELEMENT_SIGNS["air"]) == {"Gemini", "Libra", "Aquarius"}
+
+    def test_water_signs(self):
+        assert set(ELEMENT_SIGNS["water"]) == {"Cancer", "Scorpius", "Pisces"}
+
+    def test_all_signs_have_elements(self):
+        for sign in ZODIAC_SIGNS:
+            assert sign in SIGN_ELEMENTS
+
+
+class TestSignModalities:
+    def test_cardinal_signs(self):
+        assert set(MODALITY_SIGNS["cardinal"]) == {"Aries", "Cancer", "Libra", "Capricornus"}
+
+    def test_fixed_signs(self):
+        assert set(MODALITY_SIGNS["fixed"]) == {"Taurus", "Leo", "Scorpius", "Aquarius"}
+
+    def test_mutable_signs(self):
+        assert set(MODALITY_SIGNS["mutable"]) == {"Gemini", "Virgo", "Sagittarius", "Pisces"}
+
+    def test_all_signs_have_modalities(self):
+        for sign in ZODIAC_SIGNS:
+            assert sign in SIGN_MODALITIES
+
+
+# ── Helper Constants ─────────────────────────────────────────────────
+
+class TestHelperConstants:
+    def test_angular_houses(self):
+        assert ANGULAR_HOUSES == {1, 4, 7, 10}
+
+    def test_succedent_houses(self):
+        assert SUCCEDENT_HOUSES == {2, 5, 8, 11}
+
+    def test_cadent_houses(self):
+        assert CADENT_HOUSES == {3, 6, 9, 12}
+
+    def test_personal_planets(self):
+        assert PERSONAL_PLANETS == {"sun", "moon", "mercury", "venus", "mars"}
+
+    def test_hard_aspects(self):
+        assert HARD_ASPECTS == {"conjunction", "square", "opposition"}
+
+
+# ── Sample Planet Fixtures ───────────────────────────────────────────
+
+def _make_planets() -> list[dict[str, Any]]:
+    """Create a realistic 12-planet list for testing."""
+    return [
+        {"body": "sun", "sign": "Leo", "house": 5, "absolute_lon": 135.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "moon", "sign": "Cancer", "house": 4, "absolute_lon": 105.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "mercury", "sign": "Leo", "house": 5, "absolute_lon": 138.0, "degree_within_sign": 18.0, "retrograde": False},
+        {"body": "venus", "sign": "Virgo", "house": 6, "absolute_lon": 155.0, "degree_within_sign": 5.0, "retrograde": False},
+        {"body": "mars", "sign": "Aries", "house": 1, "absolute_lon": 15.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "jupiter", "sign": "Sagittarius", "house": 9, "absolute_lon": 255.0, "degree_within_sign": 15.0, "retrograde": True},
+        {"body": "saturn", "sign": "Capricornus", "house": 10, "absolute_lon": 285.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "uranus", "sign": "Aquarius", "house": 11, "absolute_lon": 315.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "neptune", "sign": "Pisces", "house": 12, "absolute_lon": 345.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "pluto", "sign": "Scorpius", "house": 8, "absolute_lon": 225.0, "degree_within_sign": 15.0, "retrograde": True},
+        {"body": "chiron", "sign": "Aries", "house": 1, "absolute_lon": 12.0, "degree_within_sign": 12.0, "retrograde": False},
+        {"body": "true_node", "sign": "Gemini", "house": 3, "absolute_lon": 75.0, "degree_within_sign": 15.0, "retrograde": False},
+    ]
+
+
+def _make_stellium_planets() -> list[dict[str, Any]]:
+    """Create a planet list with a stellium in Leo (sign) and house 5."""
+    return [
+        {"body": "sun", "sign": "Leo", "house": 5, "absolute_lon": 130.0, "degree_within_sign": 10.0, "retrograde": False},
+        {"body": "moon", "sign": "Leo", "house": 5, "absolute_lon": 135.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "mercury", "sign": "Leo", "house": 5, "absolute_lon": 140.0, "degree_within_sign": 20.0, "retrograde": False},
+        {"body": "venus", "sign": "Virgo", "house": 6, "absolute_lon": 155.0, "degree_within_sign": 5.0, "retrograde": False},
+        {"body": "mars", "sign": "Aries", "house": 1, "absolute_lon": 15.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "jupiter", "sign": "Sagittarius", "house": 9, "absolute_lon": 255.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "saturn", "sign": "Capricornus", "house": 10, "absolute_lon": 285.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "uranus", "sign": "Aquarius", "house": 11, "absolute_lon": 315.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "neptune", "sign": "Pisces", "house": 12, "absolute_lon": 345.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "pluto", "sign": "Scorpius", "house": 8, "absolute_lon": 225.0, "degree_within_sign": 15.0, "retrograde": False},
+        {"body": "chiron", "sign": "Aries", "house": 1, "absolute_lon": 12.0, "degree_within_sign": 12.0, "retrograde": False},
+        {"body": "true_node", "sign": "Gemini", "house": 3, "absolute_lon": 75.0, "degree_within_sign": 15.0, "retrograde": False},
+    ]
+
+
+# ── Element Balance ──────────────────────────────────────────────────
+
+class TestGetElementBalance:
+    def test_basic_counts(self):
+        planets = _make_planets()
+        result = get_element_balance(planets)
+        assert result["total"] == 12
+        assert "counts" in result
+        assert "percentages" in result
+
+    def test_all_elements_present(self):
+        planets = _make_planets()
+        result = get_element_balance(planets)
+        # Leo(fire), Cancer(water), Leo(fire), Virgo(earth), Aries(fire),
+        # Sagittarius(fire), Capricornus(earth), Aquarius(air), Pisces(water),
+        # Scorpius(water), Aries(fire), Gemini(air)
+        assert result["counts"]["fire"] == 5  # sun, mercury, mars, jupiter, chiron
+        assert result["counts"]["earth"] == 2  # venus, saturn
+        assert result["counts"]["air"] == 2  # uranus, true_node
+        assert result["counts"]["water"] == 3  # moon, neptune, pluto
+
+    def test_percentages_sum_to_100(self):
+        planets = _make_planets()
+        result = get_element_balance(planets)
+        pct_sum = sum(result["percentages"].values())
+        assert abs(pct_sum - 100.0) < 1.0
+
+    def test_empty_planets(self):
+        result = get_element_balance([])
+        assert result["total"] == 0
+        assert all(v == 0 for v in result["counts"].values())
+
+
+# ── Modality Balance ─────────────────────────────────────────────────
+
+class TestGetModalityBalance:
+    def test_basic_counts(self):
+        planets = _make_planets()
+        result = get_modality_balance(planets)
+        assert result["total"] == 12
+        assert "counts" in result
+        assert "percentages" in result
+
+    def test_modalities_present(self):
+        planets = _make_planets()
+        result = get_modality_balance(planets)
+        # Leo(fixed), Cancer(cardinal), Leo(fixed), Virgo(mutable), Aries(cardinal),
+        # Sagittarius(mutable), Capricornus(cardinal), Aquarius(fixed), Pisces(mutable),
+        # Scorpius(fixed), Aries(cardinal), Gemini(mutable)
+        assert result["counts"]["cardinal"] == 4  # moon, mars, saturn, chiron
+        assert result["counts"]["fixed"] == 4  # sun, mercury, uranus, pluto
+        assert result["counts"]["mutable"] == 4  # venus, jupiter, neptune, true_node
+
+    def test_empty_planets(self):
+        result = get_modality_balance([])
+        assert result["total"] == 0
+
+
+# ── Hemisphere Emphasis ──────────────────────────────────────────────
+
+class TestGetHemisphereEmphasis:
+    def test_basic_counts(self):
+        planets = _make_planets()
+        result = get_hemisphere_emphasis(planets)
+        assert sum(result.values()) == 24  # 12 planets * 2 (each counted in upper/lower AND east/west)
+
+    def test_upper_lower_sum(self):
+        planets = _make_planets()
+        result = get_hemisphere_emphasis(planets)
+        assert result["upper"] + result["lower"] == 12
+
+    def test_east_west_sum(self):
+        planets = _make_planets()
+        result = get_hemisphere_emphasis(planets)
+        assert result["east"] + result["west"] == 12
+
+
+# ── Stellium Detection ───────────────────────────────────────────────
+
+class TestDetectStelliums:
+    def test_no_stellium(self):
+        planets = _make_planets()
+        result = detect_stelliums(planets)
+        assert len(result) == 0
+
+    def test_sign_stellium(self):
+        planets = _make_stellium_planets()
+        result = detect_stelliums(planets)
+        sign_stelliums = [s for s in result if s["type"] == "sign"]
+        assert len(sign_stelliums) >= 1
+        leo_stellium = [s for s in sign_stelliums if s["key"] == "Leo"]
+        assert len(leo_stellium) == 1
+        assert set(leo_stellium[0]["planets"]) == {"sun", "moon", "mercury"}
+
+    def test_house_stellium(self):
+        planets = _make_stellium_planets()
+        result = detect_stelliums(planets)
+        house_stelliums = [s for s in result if s["type"] == "house"]
+        assert len(house_stelliums) >= 1
+        h5_stellium = [s for s in house_stelliums if s["key"] == "5"]
+        assert len(h5_stellium) == 1
+        assert set(h5_stellium[0]["planets"]) == {"sun", "moon", "mercury"}
+
+
+# ── Empty Houses ─────────────────────────────────────────────────────
+
+class TestGetEmptyHouses:
+    def test_some_empty(self):
+        planets = _make_planets()
+        result = get_empty_houses(planets)
+        occupied = {p["house"] for p in planets}
+        expected = sorted(h for h in range(1, 13) if h not in occupied)
+        assert result == expected
+
+    def test_all_occupied(self):
+        # Create planets in all 12 houses
+        planets = [{"body": f"p{i}", "house": i} for i in range(1, 13)]
+        result = get_empty_houses(planets)
+        assert result == []
+
+
+# ── Chart Ruler ──────────────────────────────────────────────────────
+
+class TestGetChartRuler:
+    def test_aries_asc_ruler_is_mars(self):
+        planets = _make_planets()
+        result = get_chart_ruler("Aries", planets)
+        assert result is not None
+        assert result["body"] == "mars"
+        assert result["sign"] == "Aries"
+
+    def test_leo_asc_ruler_is_sun(self):
+        planets = _make_planets()
+        result = get_chart_ruler("Leo", planets)
+        assert result is not None
+        assert result["body"] == "sun"
+
+    def test_scorpius_asc_modern(self):
+        planets = _make_planets()
+        result = get_chart_ruler("Scorpius", planets)
+        assert result is not None
+        assert result["body"] == "pluto"
+
+    def test_scorpius_asc_traditional(self):
+        planets = _make_planets()
+        result = get_chart_ruler("Scorpius", planets, traditional=True)
+        assert result is not None
+        assert result["body"] == "mars"
+
+    def test_ruler_not_found(self):
+        planets = [{"body": "sun", "sign": "Aries", "house": 1}]
+        result = get_chart_ruler("Aries", planets)
+        assert result is None
+
+    def test_ruler_has_retrograde(self):
+        planets = _make_planets()
+        result = get_chart_ruler("Sagittarius", planets)
+        assert result is not None
+        assert result["body"] == "jupiter"
+        assert result["retrograde"] is True
+
+
+# ── House Rulers ─────────────────────────────────────────────────────
+
+class TestGetHouseRulers:
+    def test_returns_12_entries(self):
+        planets = _make_planets()
+        houses = calculate_houses(6.0, 45.0, "placidus")
+        result = get_house_rulers(houses, planets)
+        assert len(result) == 12
+
+    def test_each_has_required_keys(self):
+        planets = _make_planets()
+        houses = calculate_houses(6.0, 45.0, "placidus")
+        result = get_house_rulers(houses, planets)
+        for entry in result:
+            assert "house" in entry
+            assert "cusp_sign" in entry
+            assert "ruler" in entry
+            assert "ruler_sign" in entry
+            assert "ruler_house" in entry
+            assert "ruler_retrograde" in entry
+
+
+# ── Group Planets by House ───────────────────────────────────────────
+
+class TestGroupPlanetsByHouse:
+    def test_grouping(self):
+        planets = _make_planets()
+        result = group_planets_by_house(planets)
+        assert 5 in result  # sun, mercury in house 5
+        assert set(result[5]) == {"sun", "mercury"}
+
+    def test_all_planets_grouped(self):
+        planets = _make_planets()
+        result = group_planets_by_house(planets)
+        total = sum(len(v) for v in result.values())
+        assert total == 12
+
+
+# ── Group Planets by Sign ────────────────────────────────────────────
+
+class TestGroupPlanetsBySign:
+    def test_grouping(self):
+        planets = _make_planets()
+        result = group_planets_by_sign(planets)
+        assert "Leo" in result
+        assert set(result["Leo"]) == {"sun", "mercury"}
+
+    def test_all_planets_grouped(self):
+        planets = _make_planets()
+        result = group_planets_by_sign(planets)
+        total = sum(len(v) for v in result.values())
+        assert total == 12
+
+
+# ── House Type Counts ────────────────────────────────────────────────
+
+class TestGetHouseTypeCounts:
+    def test_counts(self):
+        planets = _make_planets()
+        result = get_house_type_counts(planets)
+        assert result["angular"] + result["succedent"] + result["cadent"] == 12
+
+    def test_angular_count(self):
+        planets = _make_planets()
+        result = get_house_type_counts(planets)
+        # mars in h1, chiron in h1, moon in h4, saturn in h10 = 4 angular
+        assert result["angular"] == 4
+
+
+# ── Retrograde Planets ───────────────────────────────────────────────
+
+class TestGetRetrogradePlanets:
+    def test_retrograde_list(self):
+        planets = _make_planets()
+        result = get_retrograde_planets(planets)
+        names = [p["body"] for p in result]
+        assert "jupiter" in names
+        assert "pluto" in names
+
+    def test_no_retrogrades(self):
+        planets = [{"body": "sun", "retrograde": False}]
+        result = get_retrograde_planets(planets)
+        assert len(result) == 0
+
+    def test_includes_sign_and_house(self):
+        planets = _make_planets()
+        result = get_retrograde_planets(planets)
+        jupiter = [p for p in result if p["body"] == "jupiter"][0]
+        assert jupiter["sign"] == "Sagittarius"
+        assert jupiter["house"] == 9
+
+
+# ── Nodal Axis ───────────────────────────────────────────────────────
+
+class TestGetNodalAxis:
+    def test_north_node_found(self):
+        planets = _make_planets()
+        result = get_nodal_axis(planets)
+        assert result["north_node"] is not None
+        assert result["north_node"]["sign"] == "Gemini"
+
+    def test_south_node_is_opposite(self):
+        planets = _make_planets()
+        result = get_nodal_axis(planets)
+        assert result["south_node"] is not None
+        # Gemini opposite = Sagittarius
+        assert result["south_node"]["sign"] == "Sagittarius"
+
+    def test_with_houses(self):
+        planets = _make_planets()
+        houses = calculate_houses(6.0, 45.0, "placidus")
+        result = get_nodal_axis(planets, houses)
+        assert result["north_node"]["house"] is not None
+        assert result["south_node"]["house"] is not None
+
+    def test_no_node(self):
+        planets = [{"body": "sun", "sign": "Aries"}]
+        result = get_nodal_axis(planets)
+        assert result["north_node"] is None
+        assert result["south_node"] is None
+
+
+# ── Saturn Info ──────────────────────────────────────────────────────
+
+class TestGetSaturnInfo:
+    def test_saturn_found(self):
+        planets = _make_planets()
+        result = get_saturn_info(planets)
+        assert result is not None
+        assert result["body"] == "saturn"
+        assert result["sign"] == "Capricornus"
+        assert result["house"] == 10
+
+    def test_saturn_retrograde(self):
+        planets = _make_planets()
+        result = get_saturn_info(planets)
+        assert result["retrograde"] is False
+
+    def test_no_saturn(self):
+        planets = [{"body": "sun", "sign": "Aries"}]
+        result = get_saturn_info(planets)
+        assert result is None
+
+
+# ── Pluto Polarity Point ─────────────────────────────────────────────
+
+class TestGetPlutoPolarityPoint:
+    def test_ppp_is_opposite(self):
+        planets = _make_planets()
+        result = get_pluto_polarity_point(planets)
+        assert result is not None
+        # Pluto at 225° (Scorpius 15°), PPP at 45° (Taurus 15°)
+        assert result["sign"] == "Taurus"
+
+    def test_ppp_with_houses(self):
+        planets = _make_planets()
+        houses = calculate_houses(6.0, 45.0, "placidus")
+        result = get_pluto_polarity_point(planets, houses)
+        assert result["house"] is not None
+
+    def test_no_pluto(self):
+        planets = [{"body": "sun", "sign": "Aries"}]
+        result = get_pluto_polarity_point(planets)
+        assert result is None
+
+
+# ── Part of Fortune ──────────────────────────────────────────────────
+
+class TestGetPartOfFortune:
+    def test_basic_calculation(self):
+        # ASC at 90°, Sun at 135°, Moon at 105°
+        # PoF = 90 + 105 - 135 = 60° (Gemini)
+        result = get_part_of_fortune(90.0, 135.0, 105.0)
+        assert result["sign"] == "Gemini"
+
+    def test_wraparound(self):
+        # ASC at 10°, Sun at 350°, Moon at 350°
+        # PoF = 10 + 350 - 350 = 10° (Aries)
+        result = get_part_of_fortune(10.0, 350.0, 350.0)
+        assert result["sign"] == "Aries"
+
+    def test_with_houses(self):
+        result = get_part_of_fortune(90.0, 135.0, 105.0, houses=calculate_houses(6.0, 45.0, "placidus"))
+        assert result["house"] is not None
+
+
+# ── 12th House Analysis ──────────────────────────────────────────────
+
+class TestGetTwelfthHouseAnalysis:
+    def test_cusp_sign(self):
+        planets = _make_planets()
+        houses = calculate_houses(6.0, 45.0, "placidus")
+        result = get_twelfth_house_analysis(houses, planets)
+        assert result["cusp_sign"] is not None
+
+    def test_planets_in_12th(self):
+        planets = _make_planets()
+        houses = calculate_houses(6.0, 45.0, "placidus")
+        result = get_twelfth_house_analysis(houses, planets)
+        # neptune is in house 12 in our test data
+        names = [p["body"] for p in result["planets"]]
+        assert "neptune" in names
+
+    def test_ruler(self):
+        planets = _make_planets()
+        houses = calculate_houses(6.0, 45.0, "placidus")
+        result = get_twelfth_house_analysis(houses, planets)
+        assert result["ruler"] is not None
+        assert result["ruler"]["ruler"] is not None
+
+
+# ── Aspect Filtering ─────────────────────────────────────────────────
+
+class TestFilterAspectsByPlanets:
+    def test_filter_to_planets(self):
+        aspects = [
+            {"body1": "sun", "body2": "moon", "aspect": "conjunction", "orb": 2.0},
+            {"body1": "sun", "body2": "mars", "aspect": "square", "orb": 3.0},
+            {"body1": "venus", "body2": "mars", "aspect": "trine", "orb": 1.0},
+        ]
+        result = filter_aspects_by_planets(aspects, {"sun"})
+        assert len(result) == 2
+
+    def test_filter_with_aspect_types(self):
+        aspects = [
+            {"body1": "sun", "body2": "moon", "aspect": "conjunction", "orb": 2.0},
+            {"body1": "sun", "body2": "mars", "aspect": "square", "orb": 3.0},
+            {"body1": "sun", "body2": "jupiter", "aspect": "trine", "orb": 1.0},
+        ]
+        result = filter_aspects_by_planets(aspects, {"sun"}, {"conjunction", "square"})
+        assert len(result) == 2
+
+    def test_strips_prefixes(self):
+        aspects = [
+            {"body1": "transit_saturn", "body2": "natal_true_node", "aspect": "conjunction", "orb": 1.0},
+            {"body1": "p1_saturn", "body2": "p2_sun", "aspect": "opposition", "orb": 2.0},
+        ]
+        result = filter_aspects_by_planets(aspects, {"saturn"})
+        assert len(result) == 2
+
+    def test_no_match(self):
+        aspects = [
+            {"body1": "sun", "body2": "moon", "aspect": "conjunction", "orb": 2.0},
+        ]
+        result = filter_aspects_by_planets(aspects, {"pluto"})
+        assert len(result) == 0
+
+
+class TestGetNatalAspectsToPlanets:
+    def test_sorted_by_orb(self):
+        aspects = [
+            {"body1": "sun", "body2": "true_node", "aspect": "conjunction", "orb": 3.0},
+            {"body1": "moon", "body2": "true_node", "aspect": "square", "orb": 1.0},
+            {"body1": "mars", "body2": "true_node", "aspect": "opposition", "orb": 2.0},
+        ]
+        result = get_natal_aspects_to_planets(aspects, {"true_node"})
+        orbs = [a["orb"] for a in result]
+        assert orbs == sorted(orbs)
+
+    def test_filter_to_hard_aspects(self):
+        aspects = [
+            {"body1": "sun", "body2": "true_node", "aspect": "conjunction", "orb": 1.0},
+            {"body1": "moon", "body2": "true_node", "aspect": "trine", "orb": 2.0},
+        ]
+        result = get_natal_aspects_to_planets(aspects, {"true_node"}, {"conjunction", "square", "opposition"})
+        assert len(result) == 1
+        assert result[0]["aspect"] == "conjunction"
+
+
+# ── Aspect Pattern Detection ─────────────────────────────────────────
+
+class TestDetectAspectPatterns:
+    def test_no_patterns(self):
+        """Random aspects with no patterns."""
+        planets = _make_planets()
+        aspects = [
+            {"body1": "sun", "body2": "moon", "aspect": "conjunction", "orb": 2.0},
+            {"body1": "venus", "body2": "mars", "aspect": "trine", "orb": 3.0},
+        ]
+        result = detect_aspect_patterns(planets, aspects)
+        assert len(result) == 0
+
+    def test_t_square_detection(self):
+        """Sun-Moon opposition, both square Mars = T-square with Mars apex."""
+        planets = [
+            {"body": "sun", "sign": "Aries", "house": 1, "absolute_lon": 0.0},
+            {"body": "moon", "sign": "Libra", "house": 7, "absolute_lon": 180.0},
+            {"body": "mars", "sign": "Capricornus", "house": 10, "absolute_lon": 270.0},
+        ]
+        aspects = [
+            {"body1": "sun", "body2": "moon", "aspect": "opposition", "orb": 0.0},
+            {"body1": "sun", "body2": "mars", "aspect": "square", "orb": 0.0},
+            {"body1": "moon", "body2": "mars", "aspect": "square", "orb": 0.0},
+        ]
+        result = detect_aspect_patterns(planets, aspects)
+        t_squares = [p for p in result if p["type"] == "T-square"]
+        assert len(t_squares) == 1
+        assert t_squares[0]["apex"] == "mars"
+        assert set(t_squares[0]["planets"]) == {"sun", "moon", "mars"}
+        assert t_squares[0]["modality"] == "cardinal"
+
+    def test_grand_trine_detection(self):
+        """Three planets in trine, same element."""
+        planets = [
+            {"body": "sun", "sign": "Aries", "house": 1, "absolute_lon": 15.0},
+            {"body": "jupiter", "sign": "Leo", "house": 5, "absolute_lon": 135.0},
+            {"body": "saturn", "sign": "Sagittarius", "house": 9, "absolute_lon": 255.0},
+        ]
+        aspects = [
+            {"body1": "sun", "body2": "jupiter", "aspect": "trine", "orb": 0.0},
+            {"body1": "jupiter", "body2": "saturn", "aspect": "trine", "orb": 0.0},
+            {"body1": "sun", "body2": "saturn", "aspect": "trine", "orb": 0.0},
+        ]
+        result = detect_aspect_patterns(planets, aspects)
+        gt = [p for p in result if p["type"] == "Grand Trine"]
+        assert len(gt) == 1
+        assert set(gt[0]["planets"]) == {"sun", "jupiter", "saturn"}
+        assert gt[0]["element"] == "fire"
+
+    def test_yod_detection(self):
+        """Two planets in sextile, both quincunx a third."""
+        planets = [
+            {"body": "sun", "sign": "Aries", "house": 1, "absolute_lon": 15.0},
+            {"body": "jupiter", "sign": "Gemini", "house": 3, "absolute_lon": 75.0},
+            {"body": "saturn", "sign": "Scorpius", "house": 8, "absolute_lon": 225.0},
+        ]
+        aspects = [
+            {"body1": "sun", "body2": "jupiter", "aspect": "sextile", "orb": 0.0},
+            {"body1": "sun", "body2": "saturn", "aspect": "quincunx", "orb": 0.0},
+            {"body1": "jupiter", "body2": "saturn", "aspect": "quincunx", "orb": 0.0},
+        ]
+        result = detect_aspect_patterns(planets, aspects)
+        yods = [p for p in result if p["type"] == "Yod"]
+        assert len(yods) == 1
+        assert yods[0]["apex"] == "saturn"
+        assert set(yods[0]["planets"]) == {"sun", "jupiter", "saturn"}
+
+    def test_orb_limit_filters_patterns(self):
+        """Patterns with orbs exceeding the limit should not be detected."""
+        planets = [
+            {"body": "sun", "sign": "Aries", "house": 1, "absolute_lon": 0.0},
+            {"body": "moon", "sign": "Libra", "house": 7, "absolute_lon": 180.0},
+            {"body": "mars", "sign": "Capricornus", "house": 10, "absolute_lon": 270.0},
+        ]
+        aspects = [
+            {"body1": "sun", "body2": "moon", "aspect": "opposition", "orb": 0.0},
+            {"body1": "sun", "body2": "mars", "aspect": "square", "orb": 10.0},  # too wide
+            {"body1": "moon", "body2": "mars", "aspect": "square", "orb": 10.0},  # too wide
+        ]
+        result = detect_aspect_patterns(planets, aspects, orb_limit=8.0)
+        t_squares = [p for p in result if p["type"] == "T-square"]
+        assert len(t_squares) == 0
+
+
+# ── Chart Shape Detection ────────────────────────────────────────────
+
+class TestDetectChartShape:
+    def test_bundle(self):
+        """All planets within 120°."""
+        planets = [
+            {"body": "sun", "absolute_lon": 10.0},
+            {"body": "moon", "absolute_lon": 30.0},
+            {"body": "mars", "absolute_lon": 80.0},
+            {"body": "venus", "absolute_lon": 100.0},
+        ]
+        result = detect_chart_shape(planets)
+        assert result["shape"] == "bundle"
+
+    def test_bowl(self):
+        """All planets within 180°."""
+        planets = [
+            {"body": "sun", "absolute_lon": 0.0},
+            {"body": "moon", "absolute_lon": 45.0},
+            {"body": "mars", "absolute_lon": 90.0},
+            {"body": "venus", "absolute_lon": 150.0},
+        ]
+        result = detect_chart_shape(planets)
+        assert result["shape"] == "bowl"
+
+    def test_splash(self):
+        """Planets spread around the full chart."""
+        planets = [
+            {"body": "sun", "absolute_lon": 0.0},
+            {"body": "moon", "absolute_lon": 120.0},
+            {"body": "mars", "absolute_lon": 240.0},
+        ]
+        result = detect_chart_shape(planets)
+        assert result["shape"] == "splash"
+
+    def test_single_planet(self):
+        result = detect_chart_shape([{"body": "sun", "absolute_lon": 0.0}])
+        assert result["shape"] == "unknown"
+
+    def test_empty(self):
+        result = detect_chart_shape([])
+        assert result["shape"] == "unknown"
+
+    def test_largest_gap_reported(self):
+        planets = [
+            {"body": "sun", "absolute_lon": 0.0},
+            {"body": "moon", "absolute_lon": 10.0},
+            {"body": "mars", "absolute_lon": 20.0},
+        ]
+        result = detect_chart_shape(planets)
+        assert result["largest_gap"] > 0
+        assert result["occupied_arc"] > 0