Accuracy Audit Report v1
What the engine gets right, where it diverges from references, and what it does not yet verify.
Engine version 0.1.0 · measurements taken 2026-10-10 · all numbers on this page are measured results; estimates are explicitly marked estimate.
1. Scope
fengshui.engine is a Rust computation kernel compiled to WebAssembly that runs entirely in your browser. It computes: Gregorian–lunar calendar conversion (GB/T 33661-2017 leap-month rules), the 24 solar terms (節氣 jiéqì), sexagenary day pillars (干支 gānzhī), four-pillar (BaZi 八字) year/month/day/hour pillars under two year-pillar conventions, True Solar Time, Xuan Kong flying stars (玄空飛星), Eight Mansions (八宅), date selection (擇日), and magnetic declination (WMM2025).
Verification layers, all runnable from one command (verify.ps1, 21 layers): 339 Rust unit/integration tests, 308 server flow assertions, 42 pre-launch gate assertions, and the machine cross-checks below.
2. Lunar calendar vs. Hong Kong Observatory, 1901–2100
Full machine cross-check of every lunar month start, Spring Festival, leap month, and solar-term date against the Hong Kong Observatory's published tables (200 text files, 1901–2100). Results:
| Quantity | Agreement | Divergences |
|---|---|---|
| Lunar month starts (朔 shuò) | 2471 / 2474 (99.88%) | 3 — all within 5.3 min of Beijing midnight (see below) |
| Solar term dates (4795 checked) | 4795 / 4800 (99.90%) | 5 — all within 12 min of Beijing midnight |
| Leap months | 73 / 73 | — |
| Spring Festival dates | 199 / 200 | 1 (1916: new moon 5.3 min after midnight) |
Character of every divergence: not a date error but a threshold case. When the computed new moon or term instant falls within minutes of midnight, a sub-minute difference in the instant flips the civil date. The engine's instants differ from HKO's by seconds-to-minutes in those cases; every divergence we found is of this type, and each is listed with its minute-margin in the published cross-check output.
3. Four pillars vs. an independent implementation
Differential test against lunar-javascript v1.7.7 (the library named in our specification as the differential reference): 9,100 samples, deterministic and reproducible (seed 20261010) — 800 Lichun-boundary samples (every year 1901–2100, ±2/±1/0/+1 min), 4,800 month-boundary samples (all 12 jie each year), 400 Spring-Festival-boundary samples, 1,500 random samples, 1,600 day-boundary samples (22:00 / 23:00 / 23:30 / 00:01 × both day-boundary conventions).
| Pillar | Divergence | Explanation |
|---|---|---|
| Day pillar (日柱) | 0 / 9,100 | Pure arithmetic (GB/T anchor + 60-cycle); agrees exactly |
| Hour pillar (時柱) | 0 / 9,100 | Five-Rats escape (五鼠遁) recomputed independently per sample; agrees exactly |
| Year / month pillars | 360 / 9,100 — all classified | 147 are boundary-instant cases: the two implementations compute the Lichun/jie instant with second-level differences (Lichun: 39 of 200 years differ, max 38 s). 213 are a documented convention difference in the month-stem base (see dispute register). Zero unexplained. |
Day/hour zero-divergence and zero-unclassified are hard invariants asserted in the acceptance layer; a regression in either fails the build.
Second reference line — the industry baseline: the same four-pillar battery was run against sxtwl (the Shouxing calendar, the de-facto standard used by practitioner tools): 9,500 samples, day/hour divergence 0 / 9,500, and zero unexplained year/month differences — all 3,620 fall inside one documented convention window (boundary-day switch granularity, see the dispute register D-07). Two independent references, same verdict on the arithmetic pillars.
4. Reproducibility and bindings
- Cross-binding parity: the same engine compiled to WASM (browser) and to PyO3 (server) produced byte-identical JSON on 400/400 randomized requests. This is one implementation tested through two bindings — not a cross-implementation comparison.
- Third-party recompute: an independent Python implementation of JCS canonicalization + SHA-256 recomputes the
input_digestof 400/400 cases — anyone can re-verify a chart's digest without our code (see the verify page). - Convergence: solar-term solver reports the apparent longitude actually reached; for 2026 Lichun the residual is ≈ 0.0001″ of arc.
5. Geomagnetic declination
Declination uses the official World Magnetic Model WMM2025 (coefficients generated from NOAA's source), validated against NOAA's official test vectors in the test suite. The compass module also documents its own limits: declination accuracy degrades with age from the model epoch, and the model is global-validity, not region-tuned.
6. Performance
Desktop measurement (x64, Node 24, release build, 2026-10-10):
| Operation | p50 | p95 |
|---|---|---|
Full chart recompute_json (complete, incl. longitude + life trigram) | 11.8 ms | 12.5 ms |
| 24 solar terms for a year | 1.1 ms | 1.2 ms |
| Flying star chart | 0.02 ms | 0.02 ms |
| First computation after page load (load + compile + first chart) | ≈ 12–13 ms per run, 5/5 runs | |
Module size: 451,547 bytes (441 KB) WASM, cached immutable.
7. Error budget and known divergences
- Solar position: VSOP87 (truncated series) + nutation + aberration + ΔT (observed table, Espenak–Meeus extrapolation). Term instants are converged to < 0.001″ apparent longitude; the dominant uncertainty is ΔT, which is why pre-1950 threshold cases carry minute-level uncertainty.
- New moons: Meeus ch. 49 corrected terms, anchored at k=0 = JDE 2451550.260 (true new moon, not the mean value — an anchor error we made, documented, and fixed; see errata E-04).
- Enumerated and attributed divergences: 3 new-moon instants and 5 term dates vs. HKO (1901–2100) are a day-boundary convention effect, not an error: before 1929 the HKO tables use Beijing Local Mean Time (UTC+7:45:40) for the civil date, while the engine uses UTC+8. The engine's astronomical instants are independently confirmed by a third reference (pyephem) to within ≤ 2 s on every disputed case. Lichun instants differ from lunar-javascript by ≤ 38 s (39/200 years): ≈ 87% of that is ΔT extrapolation choice beyond 2025 (no ground truth exists; the largest gap, 38 s in 2051, falls 5.6 h from midnight and flips no date), and the remainder is a small systematic +13–24 s in the historical era from the truncated VSOP87 series — the only real deviation found, with zero chart-date effects in 200 years.
- Rules, not just astronomy: where the tradition itself is ambiguous (which year stem anchors the month stem under the Lunar-New-Year convention; the remaining 12 mountains of the Tigua (替卦) rule; Eight Mansions A/B derivations), we implement the alternatives explicitly and record the choice in the output's
ruleset_id. See the school dispute register.
8. What this report does not claim
- Outside 1901–2100, lunar calendars are not verified against tables. The engine hard-limits astronomical output to 1900–2200 rather than extrapolate silently.
- No low-end device measurements yet (desktop + extrapolation only).
- No differential against sxtwl (Shouxing calendar), the de-facto industry baseline — planned.
- Cross-implementation agreement ≠ correctness. Agreement with a reference shows the two agree; HKO and astronomical first principles are what make it right. Where references disagree with each other we say so instead of averaging.
- Nothing on this page is a claim about feng shui's efficacy. See Methodology for that boundary.
Reproduce everything: the acceptance harness, cross-check scripts and raw outputs are in the repository (verify.ps1, _scratch/hko_crosscheck.js, _scratch/sizhu_crosscheck.js, and their *_result.json outputs).