Add JSON output format and a separate interpreter binary
deck-engine gains --format json (language-independent, slug-based) so a new dist/interpreter-cli can score how significant a day's transits are without linking the engine itself — it depends only on the JSON shape, via its own minimal parser. Also documents the full reading pipeline end to end (CLAUDE.md, README, docs/) and adds docs/reading.md, a non-technical explanation of what a daily reading contains and means.
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
|
||||
See [`architecture.md`](architecture.md) for how these fit together.
|
||||
There are two ways to provide input (direct CLI flags, or `run.sh` +
|
||||
`user.properties`) and two output formats (`text`, `html`); the
|
||||
`DailyReading` struct is the programmatic form both outputs are rendered
|
||||
from, and the one the future watchapp will consume directly.
|
||||
`user.properties`) and three output formats (`text`, `html`, `json`); the
|
||||
`DailyReading` struct is the programmatic form all three are rendered
|
||||
from, and the one the future watchapp will consume directly. `json` is
|
||||
also `interpreter-cli`'s input format — see "The interpreter" at the
|
||||
bottom of this file.
|
||||
|
||||
## Input
|
||||
|
||||
@@ -13,7 +15,7 @@ from, and the one the future watchapp will consume directly.
|
||||
```
|
||||
deck-engine --seed <string> --birth-date YYYY-MM-DD --birth-time HH:MM
|
||||
--birth-utc-offset <hours> --birth-lat <deg> --birth-lon <deg>
|
||||
[--date YYYY-MM-DDTHH:MM] [--format text|html]
|
||||
[--date YYYY-MM-DDTHH:MM] [--format text|html|json]
|
||||
```
|
||||
|
||||
| Flag | Required | Format | Meaning |
|
||||
@@ -25,7 +27,7 @@ deck-engine --seed <string> --birth-date YYYY-MM-DD --birth-time HH:MM
|
||||
| `--birth-lat` | yes | decimal degrees | Birth latitude, north positive. |
|
||||
| `--birth-lon` | yes | decimal degrees | Birth longitude, east positive. |
|
||||
| `--date` | no | `YYYY-MM-DDTHH:MM[:SS]` | UTC moment used for today's transits and as the moment the tarot seed is drawn against. Defaults to the current system time. |
|
||||
| `--format` | no | `text` \| `html` | Output format, defaults to `text`. |
|
||||
| `--format` | no | `text` \| `html` \| `json` | Output format, defaults to `text`. `json` always uses language-independent identifiers, ignoring `--lang`. |
|
||||
| `--lang` | no | language code, e.g. `en`, `de` | Reading language, defaults to `en`. Matches a file named `<code>.lang` in `--i18n-dir` (see "Translations" below). Unknown codes or missing files fall back to English with a warning on stderr. |
|
||||
| `--i18n-dir` | no | path | Directory to look for `<lang>.lang` in. Defaults to an `i18n/` directory next to the binary itself (resolved from `argv[0]`, so `dist/deck-engine` finds `dist/i18n/` regardless of the caller's working directory). |
|
||||
|
||||
@@ -156,3 +158,93 @@ cards rendered as an image:
|
||||
image links.
|
||||
- Reversed cards get `class="reversed"`, styled with `transform:
|
||||
rotate(180deg)` in the page's inline CSS.
|
||||
|
||||
### `--format json`
|
||||
|
||||
A single JSON object mirroring `DailyReading` exactly — `natal`,
|
||||
`transits`, `spread` — using the **slug** accessors
|
||||
(`astro_body_slug()`, `astro_sign_slug()`, `astro_moon_phase_slug()`,
|
||||
`astro_aspect_slug()`, `tarot_card_slug()`, `tarot_position_slug()`)
|
||||
rather than the display-name ones, so the output is identical regardless
|
||||
of `--lang` — this is the one format meant for another program to parse
|
||||
(`dist/interpreter-cli`, see "The interpreter" below), not for a human to
|
||||
read.
|
||||
|
||||
```json
|
||||
{
|
||||
"natal": {
|
||||
"bodies": [
|
||||
{"body": "sun", "sign": "taurus", "degree_in_sign": 23.4926, "ecliptic_longitude": 53.4926, "house": 3},
|
||||
...
|
||||
],
|
||||
"ascendant_longitude": 348.4123,
|
||||
"houses": ["pisces", "aries", ...]
|
||||
},
|
||||
"transits": {
|
||||
"bodies": [
|
||||
{"body": "sun", "sign": "cancer", "degree_in_sign": 11.5012, "ecliptic_longitude": 101.5012, "house": 5},
|
||||
...
|
||||
],
|
||||
"moon_phase": "waning_gibbous",
|
||||
"aspects": [
|
||||
{"transiting_planet": "sun", "natal_planet": "moon", "type": "opposition", "orb": 3.7021},
|
||||
...
|
||||
]
|
||||
},
|
||||
"spread": {
|
||||
"positions": [
|
||||
{"position": "present", "card": "high_priestess", "reversed": true},
|
||||
...
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `bodies` is always `NUM_BODIES` (10) entries, Sun..Pluto in `Body` enum
|
||||
order; `spread.positions` is always `TAROT_SPREAD_SIZE` (10) entries in
|
||||
`CelticCrossPosition` enum order (Present, Challenge, Crown,
|
||||
Foundation, Recent Past, Near Future, Attitude, Environment, Hopes and
|
||||
Fears, Outcome — see `CLAUDE.md`'s "Tarot" section for why that's not
|
||||
the order most tutorials use).
|
||||
- `natal.houses[i]` is the sign occupying whole-sign house `i + 1`;
|
||||
`houses[0]` is the Ascendant's own sign. `transits.bodies[*].house` is
|
||||
a house number (1-12) into the **natal** chart, not a fresh "houses for
|
||||
right now" chart — same rule as the `text`/`html` output.
|
||||
- All angles are decimal degrees, `%.4f`. `aspects` may be an empty array
|
||||
(a valid "no aspects today"), but the key itself is always present.
|
||||
|
||||
## The interpreter (`interpreter/`, `dist/interpreter-cli`)
|
||||
|
||||
`dist/interpreter-cli` is a separate binary — see `CLAUDE.md`'s
|
||||
"Interpretation" section for the full architecture — that reads a
|
||||
`--format json` document and scores how significant today's transits
|
||||
are. It never links the engine; it only depends on the JSON shape
|
||||
above.
|
||||
|
||||
```bash
|
||||
dist/deck-engine ... --format json | dist/interpreter-cli
|
||||
# or:
|
||||
dist/deck-engine ... --format json > reading.json
|
||||
dist/interpreter-cli reading.json
|
||||
```
|
||||
|
||||
- **Input**: a file path argument, or stdin if no argument (or `-`) is
|
||||
given. Only `"transits"."aspects"` is read — `natal` and `spread` may
|
||||
be present (and are ignored) or omitted entirely, except that
|
||||
`"transits"."aspects"` itself must be present (an empty array is valid
|
||||
and means "no notable transits today"; a missing key is treated as
|
||||
invalid input).
|
||||
- **Output**: a plain-text report — the day's overall significance level
|
||||
(`Quiet`/`Notable`/`Significant`/`Major`), a "worth a deeper Celtic
|
||||
Cross look" line on `Major` days only, and up to 5 of today's aspects
|
||||
ranked by score:
|
||||
|
||||
```
|
||||
Day significance: Significant
|
||||
(a major transit today - worth a deeper Celtic Cross look)
|
||||
|
||||
Top 3 significant transits:
|
||||
1. Transiting Saturn Square natal Sun (orb 0.5°, score 8.10)
|
||||
2. Transiting Pluto Opposition natal Moon (orb 0.2°, score 7.92)
|
||||
3. Transiting Jupiter Trine natal Venus (orb 2.1°, score 3.40)
|
||||
```
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
# Your daily reading, explained
|
||||
|
||||
This page describes what actually happens when you get a reading —
|
||||
no code, no jargon you haven't already agreed to. If you just want to
|
||||
know "what am I looking at", start here.
|
||||
|
||||
## The one-time setup: telling it who you are
|
||||
|
||||
Before you can get a personal reading, you tell it a few things about
|
||||
your birth, once:
|
||||
|
||||
- the **date** and **time** you were born, and which time zone that
|
||||
clock time was in
|
||||
- **where** you were born (latitude/longitude)
|
||||
|
||||
That's it. This information never changes and is only used to work out
|
||||
exactly where the planets were, and which zodiac sign was rising, at the
|
||||
moment you were born — your **natal chart**. Everything else the app
|
||||
does is compared against that one fixed reference point.
|
||||
|
||||
## What happens every day
|
||||
|
||||
Each day, the app does two independent things and then combines them:
|
||||
|
||||
1. **It looks at the sky right now** and compares it to your natal
|
||||
chart from step one — this tells you what's changing today,
|
||||
relative to your own personal baseline (not a generic horoscope).
|
||||
2. **It shuffles a tarot deck**, using today's date as the shuffle
|
||||
"seed". This means: if you check your reading three times on the
|
||||
same day, you get the exact same 10 cards, in the same order, every
|
||||
time — it only changes when the date changes. There's no randomness
|
||||
involved beyond that; today's date fully determines today's spread.
|
||||
|
||||
## The three parts of a reading
|
||||
|
||||
### 1. Your natal chart
|
||||
|
||||
A snapshot of the sky at the moment you were born: where the Sun,
|
||||
Moon, and each planet were, which zodiac sign was rising on the
|
||||
eastern horizon at that moment (your **Ascendant**), and which of the
|
||||
12 astrological "houses" each planet falls into as a result. Think of
|
||||
this as your personal baseline — it doesn't change day to day, and
|
||||
every other part of the reading is measured against it.
|
||||
|
||||
### 2. Today's sky (your "transits")
|
||||
|
||||
Where the Sun, Moon, and planets are positioned *today*, plus:
|
||||
|
||||
- the current **Moon phase** (New, Waxing Crescent, Full, and so on)
|
||||
- any **aspects** — meaningful angles formed between where a planet is
|
||||
today and where a planet sat in your natal chart. For example, "the
|
||||
Sun is opposite your natal Moon today" is one aspect. These are the
|
||||
traditional signals astrologers use to say a day is emotionally
|
||||
charged, a good day for decisions, a day of tension, etc. Not every
|
||||
day has aspects; some days are quiet.
|
||||
|
||||
Each planet's position today is also placed into one of *your* 12
|
||||
houses (not a fresh chart for right now) — that's the standard way
|
||||
astrologers read "what house is this transit happening in for me".
|
||||
|
||||
### 3. Your tarot spread (the Celtic Cross)
|
||||
|
||||
A classic 10-card tarot layout, drawn from the 22 Major Arcana cards
|
||||
(The Fool, The Magician, and so on through The World). Each of the 10
|
||||
positions asks a different question about your day or situation — for
|
||||
example, "what covers you right now", "what crosses/challenges you",
|
||||
"your hopes and fears", "the likely outcome". Every card can land
|
||||
**upright** or **reversed**, which traditionally shades or inverts its
|
||||
meaning. The order and meanings of the 10 positions, and the meaning
|
||||
of each card, come from A. E. Waite's original 1911 description of the
|
||||
spread — the same source most modern tarot decks trace back to.
|
||||
|
||||
## How "big" is today?
|
||||
|
||||
Not every day is equally eventful astrologically. A separate,
|
||||
optional step can look at today's aspects (part 2, above) and works
|
||||
out an overall **significance level** for the day — Quiet, Notable,
|
||||
Significant, or Major — based on which planets are involved, how exact
|
||||
the angle is, and whether it touches your Sun or Moon (the two most
|
||||
personally weighted points in a chart). On a genuinely Major day, it
|
||||
also calls out the single most important thing happening, as a hint
|
||||
that it's worth paying closer attention to your tarot spread that day
|
||||
rather than treating it as routine. Quiet days still get a full
|
||||
reading — this step only adds a "pay attention today" flag, it never
|
||||
removes anything.
|
||||
|
||||
## What a day's reading actually gives you
|
||||
|
||||
Put together, one day's reading contains: your natal chart (for
|
||||
reference), today's sky and how it's speaking to your chart, how
|
||||
significant today is overall, and your 10-card Celtic Cross spread for
|
||||
the day. Right now these sit side by side — the astrology tells you
|
||||
*how eventful* today is and *where* to pay attention, and the tarot
|
||||
spread gives you the actual guidance to sit with. They don't yet get
|
||||
woven into a single narrative (e.g. a spread explained *in light of*
|
||||
today's significant transit) — that combined storytelling is the next
|
||||
piece to build, not something you get today.
|
||||
|
||||
*(This page, like that piece, is still a work in progress — expect it
|
||||
to grow as the reading itself does.)*
|
||||
Reference in New Issue
Block a user