Files
deck_in_a_dash/docs/input-output-format.md
T
ml 41422adf98 Add narrative text and a day-significance rank to the interpreter
Each of the day's top significant transits now gets a short sentence
describing what it classically means and which area of life its house
governs, condensed from Sepharial's Transits and Planetary Periods
(1920, public domain) plus a small table of traditional house
significations. The day-level line also shows its rank out of the 4
defined levels, e.g. "Notable (2/4)". Docs (CLAUDE.md, README,
docs/input-output-format.md, docs/reading.md) updated to match,
including a full worked example in docs/reading.md.
2026-07-05 21:05:53 +02:00

274 lines
12 KiB
Markdown

# Input & output formats
See [`architecture.md`](architecture.md) for how these fit together.
There are two ways to provide input (direct CLI flags, or `run-engine.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-engine.sh` + `user.properties`
`dist/run-engine.sh [text|html|json] [lang]` wraps the binary for daily
use:
- **Seed and `--date`** come from the OS clock: `date +%Y-%m-%d` (stable
all day) and `date -u +%Y-%m-%dT%H:%M`.
- **Birth data and language** are read from `dist/user.properties`, a
`key=value` properties file next to the script (`#`-prefixed lines and
blank lines are comments; whitespace around `=` is trimmed):
```properties
birth_date=1990-05-14
birth_time=14:32
birth_utc_offset=2
birth_lat=52.5200
birth_lon=13.4050
lang=en
```
The file is seeded once from `engine/scripts/user.properties.template`
with placeholder values (`YOUR_BIRTH_DATE_HERE`, etc.). `run-engine.sh`
checks every birth field for an empty value or a leftover `YOUR_`
placeholder and refuses to run with a clear error until the file is
filled in; `lang` has no such check since it always has a usable
default (`en`). Since `dist/` itself can be wiped (`make clean`, or
deleting the directory), the build also keeps a durable backup at
`engine/scripts/user.properties.local` (gitignored) once the file
looks filled in, and restores from it if `dist/user.properties` is
ever missing — see `CLAUDE.md`'s "Build & run".
- The optional second argument overrides `lang=` for a single run without
editing the file, e.g. `dist/run-engine.sh html de`.
- `dist/run-interpreter.sh` (no arguments) is the equivalent wrapper for
the interpreter: same OS clock + `user.properties` inputs, but calls
`deck-engine --format json` and pipes it straight into
`dist/interpreter-cli` — see "The interpreter" below.
### 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.
```c
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-sign `house` 1-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 — `house` here 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,
an `AspectType` — conjunction/sextile/square/trine/opposition — and
the orb in degrees).
- **`CelticCrossSpread`** (`tarot.h`): `positions[10]`, indexed by
`CelticCrossPosition` (Present, Challenge, Crown, Foundation, Recent
Past, Near Future, Attitude, Environment, Hopes and Fears, Outcome —
Waite's own drawing order), each holding a `TarotCard` and a `reversed`
bool.
### `--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:
```html
<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 an `img/` directory, exactly like
`dist/reading.html` next to `dist/img/` (which `make images` populates
from `res/img/`). Saving the HTML output anywhere else breaks the
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
# or, for daily use (OS clock + user.properties, same inputs as run-engine.sh):
dist/run-interpreter.sh
```
- **Input**: a file path argument, or stdin if no argument (or `-`) is
given. `"transits"."aspects"` is required (an empty array is valid and
means "no notable transits today"; a missing key is treated as invalid
input) and drives scoring. `"natal"."bodies"`/`"transits"."bodies"` are
read too, for the sign/house context printed alongside each event below
(not for scoring) — an entry naming an unrecognized body, or a missing
`house`, is silently skipped rather than failing the load (see
`parse_bodies()` in `reading_io.c`); `spread` is always ignored.
- **Output**: a plain-text report — the day's overall significance level
(`Quiet`/`Notable`/`Significant`/`Major`, plus its rank out of the 4
defined levels, e.g. `Notable (2/4)`), a "worth a deeper Celtic Cross
look" line on `Major` days only, and up to 5 of today's aspects ranked
by score, each naming the sign and natal house the transiting planet
currently occupies and the sign and house of the natal planet it's
aspecting, followed by a short narrative sentence about what that
transiting planet classically signifies *and which area of life its
current house governs* (see `CLAUDE.md`'s `narrative.c` bullet) —
omitted only if the source material has nothing to say for that
planet/aspect combination:
```
Day significance: Significant (3/4)
(a major transit today - worth a deeper Celtic Cross look)
Top 3 significant transits:
1. Transiting Saturn in Aries (house 2) Square natal Sun in Taurus (house 3) (orb 0.5°, score 8.10)
In matters of money, possessions, and personal values (house 2). Brings depression, stagnation, hindrances and obstacles, and some deprivation of the usual benefits.
2. Transiting Pluto in Aquarius (house 12) Opposition natal Moon in Capricorn (house 11) (orb 0.2°, score 7.92)
In matters of solitude, the subconscious, and hidden matters (house 12). Brings deep, often hidden transformation - the surfacing or dismantling of something that has outgrown its old form.
3. Transiting Jupiter in Leo (house 5) Trine natal Venus in Aries (house 2) (orb 2.1°, score 3.40)
In matters of romance, creativity, and children (house 5). Brings increase and expansion - fullness of fortune and health, and a generally fortunate time.
```