# Systematic test expansion — 15 October 2026

**67 automated tests pass, 0 fail, 0 skip**, in approximately 20.5 seconds on the selected machine. The visible UI and local preview route are preserved. The only runtime change caches timezone validation alongside its existing formatter cache.

## Timezones: invariants and independent rule checks

| Matrix | Scope | Result |
| --- | --- | --- |
| Node IANA zones | All 417 enumerated zones + UTC/Kolkata/Kathmandu/Kyiv aliases: **421** | Pass |
| Independent clock/display/UTC roundtrip | 34 instants per zone: **14,314 cases** | Pass |
| Independent civil-month windows | 24 selected months per zone: **10,104 windows** | Pass |
| Offset transitions | Every represented offset change during 2026: **264** | Pass |
| Actual Chrome runtime | All 418 enumerated zones + same aliases: **422** | Pass |
| Chrome UTC/local and astronomy invariants | 34 instants per zone: **14,348 cases** | Pass |
| Device defaults | **24 major zones** through application handlers | Pass |
| UI timezone edits | **72** edits across day/month/year/leap-date boundaries | Pass |

Node checks run with **Node 22.12.0, ICU 76.1, IANA 2024b**. An independent Python standard-library `ZoneInfo` implementation, reading pinned `tzdata==2024.2` (IANA 2024b), generated exact local calendar fields, UTC offsets, civil-month beginnings and gap/fold candidates. Tests compare those facts to the application’s Intl-based calculations. Matching rule versions prevents a political rule update from masquerading as an algorithm discrepancy.

The matrix includes all enumerated regions, not just a selected western DST sample. Named UI representatives cover UTC, New York, Los Angeles, St Johns, São Paulo, Santiago, London, Berlin, Cairo, Johannesburg, Casablanca, Kolkata, Kathmandu, Dubai, Tokyo, Singapore, Adelaide, Sydney, Lord Howe, Auckland, Chatham, Apia, Kiritimati and Honolulu.

Cases include midnight/month/year boundaries, leap days in 1972/2000/2024, non-leap 2100, 2016/2017 leap-offset boundaries, New York and European DST, Lord Howe’s half-hour change, fractional offsets, Liberia’s historical seconds offset, Nepal’s 1986 midnight gap, and the skipped dates in Samoa and Kwajalein. Every 2026 gap is rejected, each fold yields both correct instants, and UTC offset changes are checked immediately before and at the transition.

At the same recovered UTC instant, complete astronomical measurements and all three UTC start/end pairs must remain identical across zones. Local formatting is checked separately. Timezone invariance alone does **not** validate astronomical accuracy.

Chrome 154’s browser matrix was run through a dedicated localhost tab. Its newer runtime enumerated one more zone than Node. All its cases pass. This browser matrix checks roundtrip and UTC invariance against the actual browser implementation; it is **not** a second independent timezone-rules oracle. [Browser results](browser-timezones.json) and [screenshot](browser-timezones.jpg) are retained. No unrelated browser tab or global viewport setting was changed.

## Astronomy and inverse interaction

The expanded Swiss/Moshier oracle samples **4,510 distinct instants spanning 1972–2100**, using monthly coverage, every day of 2026 at varying times, 2,048 seeded random instants, the original sample and calendar/clock edges. It separately solves **3,372 endpoints at 562 anchor instants**. The original acceptance gates were retained without relaxation.

| Quantity | Maximum observed difference |
| --- | ---: |
| Sun tropical longitude | 2.267332″ |
| Moon tropical / sidereal longitude | 3.618799″ / 3.540426″ |
| Mean conventional Lahiri | 0.000162917″ |
| Tithi ending / starting instants | 7.750 s |
| Nakshatra instants | 6.432 s |
| Pada instants | 6.006 s |

These are finite-sample observations. The reference flags, time conversion, licenses and source versions are in [VALIDATION.md](VALIDATION.md); full error dates are in [validation-results.json](validation-results.json). The existing 10 JPL samples and 14 official IMD minute-resolution comparisons remain separate checks with their original narrower coverage.

Of **13,530 classification comparisons**, 13,517 are farther than 10″ from a reference boundary and all agree. The 13 within that margin are not asserted as exact classification agreement, because ephemeris uncertainty near a boundary can legitimately change the interval label.

For every independently sampled interval, tests check the millisecond immediately before and at both edges (**6,744 classification checks**) and verify adjacent intervals share one boundary without a gap or overlap. A continuous walk through 2026 covers 372 tithi intervals, 362 nakshatra intervals and 1,447 pada intervals, visiting every index in each cycle. These interval counts include the partial intervals at year edges.

Inverse tests use **192 month/major-zone windows**, all 27 sector centers plus wrap targets (**6,720 angle queries**), phase roots, repeated longitude candidates and exact month clamps. **48 forward/reverse drag paths with 4,320 moves** cross angular wrap, traverse repeated circuits and hit both month edges. The Sun and Moon always use the resulting shared UTC instant; teaching offsets remain separate.

## Actual failures and fixes

1. One new device-default assertion initially failed: it expected literal `Asia/Kolkata`, while Intl correctly canonicalized the device zone to `Asia/Calcutta`. The test now asserts the runtime’s resolved identifier and equivalent UTC/local behavior. Both aliases remain in the exhaustive matrix. This was a test expectation issue, not an incorrect time conversion.
2. The exhaustive run exposed redundant Intl construction inside cached timezone formatting. Validation now runs only on a formatter cache miss. The three main Node timezone loops fell from about **64.8 s to 6.0 s**, with identical independent results.
3. **No new mathematical, root, drag, independent ephemeris or timezone-rule correctness failures were found.** No tolerance was increased to obtain a pass.

## Reproduce and limits

`npm ci && npm run check && npm test` runs the offline suite; `node scripts/validation-report.mjs` regenerates the coverage/error reports. Source fixtures make the test run independent of external service availability.

The independent timezone fixture deliberately checks its IANA version. When changing Node/ICU timezone data, regenerate the oracle fixture with the matching data version rather than disabling the assertion. Optional reference regeneration:

```sh
node scripts/export-timezone-cases.mjs
PYTHONPATH=/tmp/indic-sky-timezone-oracle python3 scripts/timezone-oracle.py
PYTHONPATH=/tmp/indic-sky-ephemeris-oracle python3 scripts/extended-ephemeris-oracle.py
```

The first temporary environment contains `tzdata==2024.2`; the second contains `pyswisseph==2.10.3.2`. Neither dependency is a browser runtime dependency. The generators record the conventions; Swiss remains validation-only under its separate license.

`node scripts/browser-timezone-qa.mjs` creates an excluded temporary browser harness at `/.qa-timezones.html` on the local preview. Its result is read from the visible page; browser fixtures do not silently substitute Node results.

Future timezone policies and leap seconds can change. The runtime retains the declared future leap-offset assumption after 2026. Finite samples do not prove accuracy at every supported instant, and the IMD sample does not establish full Panchang/festival validity. Physical touchscreen testing and complete screen-reader/WCAG certification remain unperformed.
