Files
deck_in_a_dash/docs/input-output-format.md
T

258 lines
11 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.) and is **never
overwritten by later builds**. `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`).
- 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. 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)
```