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.
10 KiB
Input & output formats
See architecture.md for how these fit together.
There are two ways to provide input (direct CLI flags, or run.sh +
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
Direct CLI flags (dist/deck-engine)
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|json]
| Flag | Required | Format | Meaning |
|---|---|---|---|
--seed |
yes | any string | Tarot seed. The same seed always produces the same 10-card spread, positions, and orientations (see rng.c/tarot.c). |
--birth-date |
yes | YYYY-MM-DD |
Birth date, local calendar. |
--birth-time |
yes | HH:MM (24h) |
Birth time, local clock. |
--birth-utc-offset |
yes | decimal hours, e.g. 2 or -5.5 |
Hours to subtract from local birth time to get UTC (e.g. 2 for CEST). |
--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 | 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). |
Birth latitude/longitude only affect the Ascendant/houses — planet signs are geocentric and location-independent.
run.sh + user.properties
dist/run.sh [text|html] [lang] wraps the binary for daily use:
-
Seed and
--datecome from the OS clock:date +%Y-%m-%d(stable all day) anddate -u +%Y-%m-%dT%H:%M. -
Birth data and language are read from
dist/user.properties, akey=valueproperties file next to the script (#-prefixed lines and blank lines are comments; whitespace around=is trimmed):birth_date=1990-05-14 birth_time=14:32 birth_utc_offset=2 birth_lat=52.5200 birth_lon=13.4050 lang=enThe file is seeded once from
engine/scripts/user.properties.templatewith placeholder values (YOUR_BIRTH_DATE_HERE, etc.) and is never overwritten by later builds.run.shchecks every birth field for an empty value or a leftoverYOUR_placeholder and refuses to run with a clear error until the file is filled in;langhas no such check since it always has a usable default (en). -
The optional second argument overrides
lang=for a single run without editing the file, e.g.dist/run.sh html de.
Translations (--lang, dist/i18n/)
Every piece of display text — planet/sign/moon-phase/aspect names, tarot
card names and meanings, Celtic Cross position names, and the section
headers in the text/html output — is looked up in a translation
catalog with the built-in English text as the fallback for any key the
catalog doesn't have. See CLAUDE.md's "Translations" section for the
key-naming convention and how to add a new language.
engine/i18n/en.lang and de.lang are the shipped translation files;
make copies engine/i18n/*.lang to dist/i18n/ (refreshed on every
build, like dist/img/). Adding a language is just dropping another
<code>.lang file into engine/i18n/ — no code changes needed.
Output
DailyReading (the real output — reading.h)
reading_generate() is the actual API; text/html below are just two
ways of rendering the struct it fills in. This is what the watchapp will
read directly once it exists.
typedef struct {
NatalChart natal;
DailyTransits transits;
CelticCrossSpread spread;
} DailyReading;
NatalChart(astro.h):bodies[10](Sun..Pluto, each a sign + degree-in-sign + raw ecliptic longitude + whole-signhouse1-12),ascendant_longitude,houses[12](whole-sign:houses[0]is the Ascendant's sign).DailyTransits(astro.h):bodies[10](today's positions, same shape as above —househere is the natal chart's house the transiting planet currently occupies, not a fresh "houses for right now" chart),moon_phase(one of 8 named phases),aspects[]+aspect_count(each aspect names a transiting planet, a natal planet, anAspectType— conjunction/sextile/square/trine/opposition — and the orb in degrees).CelticCrossSpread(tarot.h):positions[10], indexed byCelticCrossPosition(Present, Challenge, Crown, Foundation, Recent Past, Near Future, Attitude, Environment, Hopes and Fears, Outcome — Waite's own drawing order), each holding aTarotCardand areversedbool.
--format text
Four =====-delimited sections, in order: natal chart (each body's sign
- degree + house, then the Ascendant), today's sky (Moon phase, then each
body's transiting position + the natal house it currently occupies),
today's aspects to the natal chart (one line per aspect, with orb), and
the Celtic Cross (one block per position: name, card,
(Reversed)if applicable, the position's meaning, then the card's meaning).
===== Natal Chart =====
Sun 23.5° Taurus (House 3)
Moon 15.2° Capricorn (House 11)
...
Ascendant 18.4° Pisces
===== Today's Sky =====
Moon phase: Waning Gibbous
Sun 11.5° Cancer (House 5)
...
===== Today's Aspects to Natal Chart =====
Transiting Sun Opposition natal Moon (orb 3.7°)
...
===== Celtic Cross =====
The Present The High Priestess (Reversed)
This covers him: the general influence affecting the matter.
Passion, moral or physical ardour, conceit, surface knowledge.
...
--format html
A single self-contained HTML page (inline <style>, no JS, no external
requests) with the same four sections, plus each of the 10 Celtic Cross
cards rendered as an image:
<img class="reversed" src="img/RWS_Tarot_02_High_Priestess.jpeg" alt="The High Priestess">
src="img/<file>"is relative to the HTML file's own location — it expects to sit next to animg/directory, exactly likedist/reading.htmlnext todist/img/(whichmake imagespopulates fromres/img/). Saving the HTML output anywhere else breaks the image links.- Reversed cards get
class="reversed", styled withtransform: 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.
{
"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},
...
]
}
}
bodiesis alwaysNUM_BODIES(10) entries, Sun..Pluto inBodyenum order;spread.positionsis alwaysTAROT_SPREAD_SIZE(10) entries inCelticCrossPositionenum order (Present, Challenge, Crown, Foundation, Recent Past, Near Future, Attitude, Environment, Hopes and Fears, Outcome — seeCLAUDE.md's "Tarot" section for why that's not the order most tutorials use).natal.houses[i]is the sign occupying whole-sign housei + 1;houses[0]is the Ascendant's own sign.transits.bodies[*].houseis a house number (1-12) into the natal chart, not a fresh "houses for right now" chart — same rule as thetext/htmloutput.- All angles are decimal degrees,
%.4f.aspectsmay 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.
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 —natalandspreadmay 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 onMajordays 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)