Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ never by hand.
| Command | Result |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pnpm run typecheck` | clean (tsc strict over packages, tools, tests) |
| `pnpm run test` | 556 tests / 31 files �all passing (all systems + JPL Horizons 独立 golden + interpret + 吉凶 + 合婚 + reading-lint/空话/重复/越界 + validate-answer v2 结构与措辞门禁(约束引用事实豁免+全可见文本安全扫�资源上限+有界解析入口,非语义正确性证明) + western-rules/ziwei-rules 语义规则 + 版本迁移/回滚/目标白名�+ PII 隐私护栏 green) |
| `pnpm run test` | 675 tests / 33 files �all passing (all systems + JPL Horizons 独立 golden + interpret + 吉凶 + 合婚 + reading-lint/空话/重复/越界 + validate-answer v2 结构与措辞门禁(约束引用事实豁免+全可见文本安全扫�资源上限+有界解析入口,非语义正确性证明) + western-rules/ziwei-rules 语义规则 + 版本迁移/回滚/目标白名�+ PII 隐私护栏 green) |
| `pnpm run build` | `engine.mjs` ï¿?2.8 MB + `sbom.cdx.json` + `sbom.spdx.json` (6 runtime deps) |
| `pnpm run validate:skill` | 40 / 40 (incl. scripts/ no-stray-files guard + CycloneDX/SPDX SBOM checks + validate-answer/lint-reading gate-workflow doc checks) |
| `pnpm run validate:reading` | 53 / 53 (topic example libraries + output-spec structure + 无术语区 firewall; offline, no LLM) |
Expand Down Expand Up @@ -216,9 +216,10 @@ runnable from a clean copy outside the repo, offline, deterministic.
## Open risks

- Western angles/houses are derived in-house (not astronomy-engine); they are validated against
the MC=RAMC and eastern-horizon oracles, but an independent golden house-table (e.g. a Swiss
Ephemeris reference chart) would further harden them. (Planetary longitudes now HAVE an
independent JPL Horizons golden; only the house layer still lacks one.)
the MC=RAMC and eastern-horizon oracles and an independent Swiss Ephemeris house golden
(swetest 2.10.03, 5 synthetic cases x 5 systems, 12 cusps + angles each, worst measured
deviation 0.69 arc-minutes; see packages/western/goldens/README.md). The golden covers
normal latitudes; circumpolar instants are contract-tested (HOUSE_SYSTEM_UNAVAILABLE).
- LMT-era (early 1901+) offsets are whole-minute precision (moment-timezone limitation).
- Equation of time is an approximation (~tenths of a minute); fine for display, not high-precision.
- TypeScript pinned to 5.9 while registry `latest` is 7.0; revisit after TS 7 soaks.
Expand Down
8 changes: 4 additions & 4 deletions docs/VALIDATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ the identical table in [STATUS.md](./STATUS.md) ("Commands & results"). Do not h
resolve a disagreement; re-run the suite and copy the actual count. `pnpm run check:doc-counts`
re-runs the suite and fails if either doc's `N tests / M files` count drifts from the real run.

- Typecheck: clean. Tests: **556 tests / 31 files ï¿?all passing**. The Western provider
- Typecheck: clean. Tests: **675 tests / 33 files ï¿?all passing**. The Western provider
(astronomy-engine, VSOP87 + NOVAS) passes the ADR-0003 ï¿?ï¿?gate two ways: wrapper-consistency
(vs astronomy-engine's own output) plus an **independent JPL Horizons golden** (10 bodies × 3
technical epochs fetched from the NASA/JPL Horizons service, query recorded in
Expand Down Expand Up @@ -133,11 +133,11 @@ canonical-JSON order independence; deterministic hashing; calculate determinism.
why it is trustworthy.
- Western: at least one set cross-checked against JPL/Swiss; main-body positions within ï¿?
arc-minute; discrete classifications (sign/house changes) must match exactly. (Planetary
longitudes: met via the JPL Horizons golden; an independent house table is still open.)
longitudes: met via the JPL Horizons golden; house cusps: met via the Swiss Ephemeris house golden.)
- BaZi/Zi Wei: sourced references, NOT another wrapper of the same core library.
- Snapshots guard against regressions only; they never become ground truth.
- Minimum targets: time/location 30 (met: 36), Western 20 (met: 30 ï¿?JPL Horizons golden
longitudes), BaZi 40, Zi Wei 20.
- Minimum targets: time/location 30 (met: 36), Western 20 (met: 55 ï¿?JPL Horizons golden
longitudes + 25 Swiss Ephemeris house-cusp sets), BaZi 40, Zi Wei 20.

## Must-test boundaries (tracked to Phase 2)

Expand Down
88 changes: 85 additions & 3 deletions packages/western/goldens/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Western independent golden fixtures (JPL Horizons)
# Western independent golden fixtures (JPL Horizons + Swiss Ephemeris)

This directory holds the **independent** ephemeris cross-check of the Western provider
against authoritative [JPL Horizons](https://ssd.jpl.nasa.gov/horizons/) geocentric
Expand All @@ -21,9 +21,91 @@ that layer, plus the Sun cardinal-point anchors).
Do **not** fabricate values here. When adding rows or epochs, copy them from a real,
reproducible JPL Horizons query and record the exact query in the fixture's `source` block.

## Swiss Ephemeris house golden — POPULATED

`swiss-ephemeris-houses.json` is the independent house-cusp golden (placidus / koch /
porphyry / equal / whole-sign) referenced against the Swiss Ephemeris `swetest`
command-line tool. **Populated** (swetest `Version: 2.10.03`, captured 2026-07-28,
input time scale UTC via `-utc`): 5 synthetic technical epochs × 5 systems × 12 cusps
plus ASC/MC/ARMC, transcribed deterministically from a reviewed local capture whose
per-file SHA-256 (stdout, stderr and argv of all 25 calls + version banner) is recorded
in the fixture's `source.rawSha256`. Measured worst deviation at population time:
placidus 0.286′, koch 0.688′, porphyry/equal/whole-sign 0.187′ — per-system
`toleranceArcmin` is the measured maximum plus headroom (0.5′ / 0.9′ / 0.35′), all under
the ≤1′ gate (rationale recorded in `toleranceRationale`; the residual is dominated by
mean-vs-true obliquity). `western-house-golden.test.ts` keeps its fail-closed capture
gate: a de-populated or half-filled fixture turns the suite red again.

Do **not** edit golden values by hand; regenerate via `tools/generate-house-golden.ts`
and re-review.

### Source & licensing boundary

- `swetest` is used **only** as a one-off external reference generator. Its numeric output
(factual astronomical data for given inputs) is recorded here; the swetest program,
sources, binaries and ephemeris data files never enter this repository, the lockfile,
the SBOM, the release bundle or CI. Tests run fully offline from the tracked fixture.
- The reference commands use `-emos` (Moshier). This round's reference commands do not
require bringing external ephemeris data files into the repository; no additional
precision claim is made here — measured tolerances are recorded in the fixture only
after a real capture.

### Capture workflow (two-phase)

1. On a machine with a local `swetest` install, run:

PowerShell:

```
$env:SWETEST_PATH = 'C:\path\to\swetest.exe'; node --experimental-strip-types tools/generate-house-golden.ts
```

POSIX:

```
SWETEST_PATH=/path/to/swetest node --experimental-strip-types tools/generate-house-golden.ts
```

The script refuses to run without `SWETEST_PATH` (it never downloads anything) and
invokes `swetest -b<DD.MM.YYYY> -utc<HH:MM:SS> -house<lonE>,<lat>,<LETTER> -emos -head`
once per sample × system (letters: P=placidus, K=koch, O=porphyry, E=equal(Asc),
W=whole-sign), for 5 synthetic technical epochs (see the sample matrix in the script;
none of them is real birth data).

2. All output is first written to an untracked staging directory
(`.tmp/house-golden-raw.staging-<utc>/`) and atomically renamed to
`.tmp/house-golden-raw/` only after every invocation succeeds and parses; a
pre-existing capture directory is renamed aside (never deleted, never merged), so
stale raw files cannot mix into a fresh capture. Contents:
`case-<id>-<system>.stdout.txt` / `.stderr.txt` per invocation,
`version.stdout.txt` / `version.stderr.txt`, `manifest.json` (full argv per call,
swetest version line, UTC capture time, separate SHA-256 for every stdout/stderr/argv,
sample matrix) and `draft-fixture.json` (parsed draft). The script exits non-zero —
producing no final directory — on any spawn error, signal, non-zero exit,
error-looking stderr, parse failure, missing field, or a quadrant-system output
mentioning "Porphyry" on either stream (swetest's silent high-latitude fallback,
which this project **rejects**).
3. The capture is then reviewed (manifest ↔ raw SHA-256 cross-check, draft ↔ raw
cross-check, order-of-magnitude sanity against engine output) before being transcribed
into the tracked fixture with `status: "populated"`, real `source.version` /
`captureDateUtc` / `rawSha256`, and per-system `toleranceArcmin` set to the smallest
justified value ≤ 1 arc-minute (rationale recorded in `toleranceRationale`).

### High-latitude boundary

The high-latitude tests in `western-house-golden.test.ts` are fixture-independent engine
self-assertions. The contract for the quadrant systems (Placidus, Koch) is: whenever the
geometry is undefined they must throw `HOUSE_SYSTEM_UNAVAILABLE` — never silently fall
back (swetest falls back to Porphyry there; this project rejects that fallback). Koch's
guard is the MC degree's own semi-arc (undefined when the MC degree is circumpolar,
|tan φ · tan δ_MC| > 1); the suite asserts it on synthetic instants VERIFIED against the
implementation to hit that condition. Whether koch is defined at a given high-latitude
instant depends on λ_MC at that moment — no blanket "always fails" claim is made.

## Not covered (still open)

- An independent golden **house table** (e.g. a Swiss Ephemeris reference chart) for the
in-house angles/houses — tracked in `docs/STATUS.md` Open risks.
- The house golden covers normal latitudes only (five synthetic cases between 33.9°S and
59.9°N); circumpolar instants are covered by contract tests (throw, no fallback), not
by golden values.
- The sidereal zodiac, true node and asteroids remain `precision: approximate` with their
own continuity regression; they are intentionally outside this ≤1′ golden.
Loading
Loading