I spend a lot of time around astrology apps — not because I'm a true believer, but because the engineering behind them is almost always terrible. You type your birth data into a web form, a black box churns somewhere, and out comes a chart you can't reproduce, can't audit, and can't trust came from the same math twice. Change one server-side library and your "destiny" silently shifts. That bothered me more than any horoscope ever could.
So I built life-chart-engine: an offline, AGPL-3.0 CLI that takes one birth record and computes three independent systems in a single pass — Western natal (Tropical zodiac, Placidus houses, via Swiss Ephemeris), 人類圖 Human Design (type, authority, profile, definition, incarnation cross, all 64 gates), and 紫微斗數 Zi Wei Dou Shu (12 palaces, star brightness, 四化 transformations, five-elements bureau, via py-iztro).
The astrology is the domain. The story is determinism.
The reproducibility problem
A birth chart is, mechanically, just a coordinate transform: given an instant in time and a point on Earth, where were the planets? That's solved astronomy. Swiss Ephemeris — the same DE431-derived ephemeris professional software uses — gives you sub-arcsecond planetary positions. There is a correct answer, and it doesn't depend on which startup's API you hit.
Black-box web apps throw that away. You can't diff two runs, you can't pin a version, you can't hand someone your inputs and have them recompute your outputs. life-chart-engine is built the opposite way: no web service, no API, no account, no telemetry. It never touches the network. Same birth record in, byte-for-byte identical chart out, every time, on any machine. That's the whole point — provenance you can audit.
Install
curl -fsSL https://raw.githubusercontent.com/zhenheco/life-chart-engine/main/install.sh | bash
The installer pins a CPython 3.12 virtualenv, and that pin is load-bearing. py-iztro (the Zi Wei Dou Shu engine) ships native dependencies that didn't yet build cleanly against newer CPython when I cut this — so rather than let users hit a wall of compiler errors on whatever interpreter their distro ships, the installer provisions an isolated 3.12 environment. It's the unglamorous reality of standing on native code: you pin the interpreter you actually tested against, you document why, and you stop pretending "latest" is free.
The --json envelope
The human-readable output is nice, but the interesting mode is --json. Every run emits a structured envelope — inputs echoed back, then the three systems as separate, labeled blocks:
life-chart --date 1990-06-15 --time 08:30 --tz 8 \
--lat 25.0330 --lon 121.5654 --json
{
"ok": true,
"schema_version": "1.0",
"input": {
"name": "Sample", "gender": "女",
"date": "1990-06-15", "time": "08:30",
"tz_offset": 8.0, "lat": 25.033, "lon": 121.5654,
"target": "2025-01-01"
},
"western": {
"system": "Tropical / Placidus / Moshier",
"ascendant": { "sign": "獅子", "deg": 8, "min": 29, "label": "獅子 08°29'" },
"planets": [
{ "name": "太陽", "sign": "雙子", "deg": 23, "min": 40, "house": 11, "retrograde": false }
]
},
"human_design": {
"type": "投射者",
"authority": "自我投射型權威",
"profile": "1/3",
"definition": "一分人",
"incarnation_cross": "右角度交叉(12/11 | 36/6)",
"design_date": "1990-03-16",
"gates": [
{ "planet": "☉", "personality": { "gate": 12, "line": 1 }, "design": { "gate": 36, "line": 3 } }
]
},
"ziwei": {
"five_elements_class": "土五局",
"soul": "祿存",
"body": "火星",
"palaces": [
{ "name": "命宮", "ganzhi": "戊寅", "major_stars": ["七殺(廟)"] }
]
},
"meta": { "engine": "life-chart-engine", "version": "1.0", "ephemeris": "Moshier" }
}
(Trimmed for readability — arrays like planets, gates, and palaces are fuller in a real run; sign and star names come out in Chinese. Note --tz is a numeric UTC offset, not an IANA name.)
One thing I'm quietly proud of is the Human Design 88° solver. Human Design needs a second "design" chart cast at the moment the Sun was exactly 88 degrees of arc before the birth position — roughly three months prior, but you can't just subtract 88 days because the Sun's apparent speed varies across the year. So it's a root-finding problem: search for the timestamp t where sun_longitude(birth) − sun_longitude(t) = 88°, then recompute the gate activations at that instant. It's a tiny numerical solver wrapped around the ephemeris, and it's exactly the kind of thing a black-box app hides and you can never verify. Here it's in the source.
Wiring it into an AI agent via AGENTS.md
Here's where the determinism pays off for AI builders. Ask a language model to "calculate my natal chart" and it will hallucinate one — plausible-sounding planetary positions with no computation behind them. That's worse than useless; it's confidently wrong.
The fix is to treat the chart as a compute primitive, not a generation task. life-chart-engine ships an AGENTS.md tool contract that tells an agent exactly how to shell out to the CLI and parse the --json envelope. Drop it in your repo and Claude Code (or any AGENTS.md-aware agent) stops guessing and starts calling:
## Tool: life-chart-engine
When the user asks for an astrological/Human Design/Zi Wei chart,
DO NOT compute or estimate it yourself. Shell out to the CLI:
life-chart --date <YYYY-MM-DD> --time <HH:MM> --tz <UTC offset, e.g. 8> \
--lat <float> --lon <float> --json
Parse the JSON envelope. Use `western`, `human_design`, and `ziwei`
blocks as ground truth. Never fabricate planetary positions or gates.
If birth time is unknown, say so — houses and ascendant require it.
Now the agent is a thin interpreter over deterministic numbers. The math is auditable; the LLM only does the part it's good at — explaining.
Cross-system triangulation (and the honesty clause)
Why three systems? Because three independent computations on the same birth moment give you a cheap confidence signal. When Western, Human Design, and Zi Wei independently point at the same theme, that's a strong signal. When only one system surfaces a detail, treat it as reference, not gospel.
To be clear about what this is: an interpretive self-awareness tool, not fatalistic prediction. No medical, legal, or financial advice — the README says so, and I mean it. The engine computes; meaning is yours.
The docs ship in 19 languages, the whole thing is AGPL-3.0, and it works on a plane with the Wi-Fi off. If you build AI agents, the takeaway generalizes past astrology: when there's a correct answer, give your model a deterministic primitive to call instead of a prompt to improvise on. That's the real lesson — domains with ground truth deserve auditable compute, not confident guesses.
Repo: https://github.com/zhenheco/life-chart-engine — issues and PRs welcome.
Top comments (0)