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:
ml
2026-07-05 15:55:43 +02:00
parent 57deb62b51
commit bd739ccfdf
25 changed files with 1497 additions and 11 deletions
+44
View File
@@ -94,6 +94,49 @@ static void test_reading_generate_smoke(void) {
printf("PASS test_reading_generate_smoke\n");
}
static void test_reading_print_json_shape(void) {
BirthData birth = {
.year = 1990, .month = 5, .day = 14,
.hour = 14, .minute = 32,
.utc_offset_hours = 2.0,
.latitude = 52.52, .longitude = 13.405,
};
DailyReading r;
reading_generate("smoke-test-seed", &birth, 1751500000, &r);
FILE *tmp = tmpfile();
assert(tmp);
reading_print_json(&r, tmp);
rewind(tmp);
char buf[16384];
size_t n = fread(buf, 1, sizeof buf - 1, tmp);
buf[n] = '\0';
fclose(tmp);
/* Loose structural checks - not a full JSON validator, just enough to
* catch a broken serializer (unbalanced braces, a missing section, or
* a stray astro_*_name()/tarot_*_name() call leaking translated text
* into what must stay language-independent regardless of --lang). */
assert(buf[0] == '{');
assert(strstr(buf, "\"natal\"") != NULL);
assert(strstr(buf, "\"transits\"") != NULL);
assert(strstr(buf, "\"spread\"") != NULL);
assert(strstr(buf, "\"aspects\"") != NULL);
assert(strstr(buf, "\"body\": \"sun\"") != NULL); /* slug, not the display name */
assert(strstr(buf, "\"Sun\"") == NULL); /* never a *_name() value */
int braces = 0;
for (size_t i = 0; i < n; i++) {
if (buf[i] == '{') braces++;
if (buf[i] == '}') braces--;
}
assert(braces == 0);
printf("PASS test_reading_print_json_shape\n");
}
/* Run with cwd == engine/ (as `make test` does), so the shipped
* engine/i18n/ .lang files are reachable as i18n/<code>.lang. */
static void test_i18n_fallback_and_translation(void) {
@@ -130,6 +173,7 @@ int main(void) {
test_tarot_determinism();
test_natal_sun_sign();
test_reading_generate_smoke();
test_reading_print_json_shape();
test_i18n_fallback_and_translation();
printf("All smoke tests passed.\n");
return 0;