A bug report against our chart engine turned out to be wrong. Auditing it turned up a different bug that nobody had reported: every retrograde station the engine computed fired about twelve hours early. Six stations across four planets, all biased between 11 h 55 m and 12 h 02 m, and the size of the error was predictable from the code before it was measured. This post is the working: what was checked, against what, what we got wrong along the way, and one question about Jyotish convention that we deliberately did not settle.
Dates matter here. The audit below was run on 26 July 2026 and the tables are its results. The engine has moved since, and where it has, this post says so rather than quietly restating July as though it were today.
Quick Facts
| Item | Detail |
|---|---|
| Audit date | 26 July 2026, against the engine as it stood then |
| External reference | JPL Horizons, DE441, geocentric apparent ecliptic longitude of date |
| Reported bug | Refuted. The chart was right, and was right before the fix too |
| Actual bug found | Every station fired about 12 hours early |
| Cause | A forward difference where a centred one was needed |
| Predicted bias | Half the baseline, under a local quadratic approximation |
| Measured bias | -11 h 55 m to -12 h 02 m across six stations |
| Verification sweep | 1,205 sampled classifications: 5 disagreements before, 0 after |
| Since fixed separately | The UT-versus-TT gap noted in July was closed on 16 August 2026 |
The reported bug was not a bug
A pinned test chart (15 January 1990, 12:00 IST, New Delhi) showed Venus (R). Venus's 1990 retrograde period was believed to have begun a few days after that date, which would have made the flag wrong.
Pulling daily geocentric apparent longitude for Venus from JPL Horizons settles it immediately. Through the fixture date the longitude decreases monotonically at about 0.58 degrees per day: 303.64 on 10 January, 300.98 on the 15th, 297.94 on the 20th. A planet whose longitude is falling is retrograde.
Sampling the turning points at 30-minute resolution and fitting a parabola at the vertex puts Venus's retrograde arc at 29 December 1989 08:50:05 UT to 8 February 1990 09:16:22 UT, a span of 41.0 days. At the fixture moment Venus was 17 days into that arc, not days short of entering it. Mercury was 15 days into its own 20.2-day arc. Jupiter was about 40 days from its station. All three flags were correct, and they were correct under the old implementation too.
The real defect: a forward difference
The engine decided retrograde status by comparing a planet's apparent longitude at the chart moment against its longitude one day later. That measures average motion over the following day, not motion now.
Near a station the planet slows, stops and reverses, so longitude is well approximated by a quadratic in time. Under that approximation a forward difference over a one-day baseline changes sign half a day before the station itself. Real longitude curves carry higher-order terms, so the prediction is approximately twelve hours early, not exactly so. What matters is that the bias is a property of the estimator rather than of any particular planet, and its scale is knowable from the code alone.
Measured against the Horizons stations:
| Station | Reference (DE441) | Old forward difference | Fixed centred difference |
|---|---|---|---|
| Mercury, direct to retrograde | 1989-12-30 23:29:08 UT | -12 h 02 m | +0.9 min |
| Mercury, retrograde to direct | 1990-01-20 04:31:30 UT | -11 h 55 m | +0.9 min |
| Venus, direct to retrograde | 1989-12-29 08:50:05 UT | -12 h 00 m | +1.0 min |
| Venus, retrograde to direct | 1990-02-08 09:16:22 UT | -11 h 58 m | +0.9 min |
| Jupiter, retrograde to direct | 1990-02-24 19:11:00 UT | -11 h 56 m | +4.2 min |
| Saturn, direct to retrograde | 1990-05-04 22:41:15 UT | -11 h 57 m | +2.9 min |
Six stations, four planets, every one early by within seven minutes of half a day. That is the signature of an estimator error rather than an ephemeris error.
The fix, and how the step was chosen
The correction is one line: sample longitude 30 minutes either side of the chart moment and compare those, instead of now against tomorrow. The guard is that the samples must stay centred on the moment being asked about.
The step was picked by measurement rather than taste. The parameter below is the half-step, so the chosen 30-minute half-step spans one hour end to end:
| Estimator | Half-step | Total span | Worst station error |
|---|---|---|---|
| Forward (old) | n/a, 1-day baseline | 1 day | 722 min |
| Centred | 12 hours | 1 day | 5.1 min |
| Centred (chosen) | 30 minutes | 1 hour | 4.2 min |
| Centred | 1 hour | 2 hours | 4.2 min |
Then the whole window was swept: five planets, daily, from 1 November 1989 to 1 July 1990, giving 1,205 sampled classifications of the engine flag against a Horizons-derived direction. The reference direction at each sample is itself a centred difference across the neighbouring daily rows, which is second-order accurate but still an estimate rather than an instantaneous measurement. On that basis the old forward difference disagreed on five samples, every one of them a day containing a station, and the centred difference disagreed on none.
What we got wrong, and withdrew
An earlier version of this audit stated that retrograde status is frame-independent, so that sidereal versus tropical does not enter into it. That is false near a station, and it has been withdrawn.
The reason is not that a slow planet takes longer to cross some angular offset. A constant offset cancels out of a derivative entirely. What survives is the ayanamsa's rate. Sidereal longitude is tropical minus the ayanamsa, so sidereal motion is zero where tropical motion equals the ayanamsa's drift rather than where it equals zero. Near a station tropical motion is climbing away from zero at roughly the longitude's local acceleration, so the timing shift is approximately the ayanamsa's rate divided by that acceleration. Slow planets have the flattest curves and therefore the smallest acceleration, which is why the shift grows for them.
Measured in July by bisecting a centred one-minute difference on tropical longitude, and then on tropical minus the mean Lahiri ayanamsa:
| Planet | Tropical station (UT) | Sidereal station, mean ayanamsa (UT) | Shift |
|---|---|---|---|
| Mercury | 1989-12-30 23:30:03 | 1989-12-30 23:29:46 | -0.30 min |
| Venus | 1989-12-29 08:51:08 | 1989-12-29 08:49:44 | -1.41 min |
| Mars | 1990-10-20 19:30:26 | 1990-10-20 19:26:44 | -3.69 min |
| Jupiter | 1990-02-24 19:15:14 | 1990-02-24 19:31:44 | +16.51 min |
| Saturn | 1990-05-04 22:44:07 | 1990-05-04 22:10:19 | -33.79 min |
That table is historical, and the convention it uses is not the one the site runs today. It subtracts the mean Lahiri ayanamsa, which is exactly linear in time. The chart path now converts with the true ayanamsa, mean plus nutation in longitude, because our tropical longitudes are apparent and already carry nutation. Under that conversion Saturn's shift is about -7.70 minutes, not -33.79, stable to about a twentieth of a minute across half-steps from 30 minutes down to 10 seconds. That change landed in commit eeb8a2d on 16 August 2026, which moved the conversion and split UT from TT together. The other planets were not re-measured under the true conversion, so treat only the Saturn figure as current.
Note also what the flag actually does: it is computed on tropical longitudes and no ayanamsa enters the difference at all. So this whole section is about what a sidereal-motion flag would do, not about a discrepancy in the shipped one. Whether Vakri and Margi ought to be judged in the sidereal frame is a genuine open question, and this audit deliberately did not settle it. If you work with this professionally and have a considered view, we would like to hear it.
Why this was worth fixing
A retrograde flag looks like display text, and mostly it is. Planet tables, chart renderings and PDF exports all just print it. But four consumers read it as an input rather than showing it:
- Combustion (asta) orb selection, which uses a different orb for a retrograde planet.
- Neecha Bhanga rule NB-7, "debilitated planet retrograde", which feeds the live Neecha Bhanga Raja Yoga tool.
- Strength-contested notes on formed Dhana and Raja yogas.
- The per-yoga note in Panch Mahapurusha.
So a chart cast in the roughly twelve-hour window before a true station could have flipped a Neecha Bhanga verdict or a combustion classification. Narrow, but real, and not something a reader would have any way to notice.
DesiUtils toolNeecha Bhanga Raja Yoga CalculatorThe tool whose verdict this flag can changeResiduals, and what has changed since
After the fix the estimator landed within about a minute on Mercury and Venus, and three to four minutes on Jupiter and Saturn.
In July the leading suspect for the ~1 minute was delta-T: the module was handed Julian Date in UT where the underlying series expects Terrestrial Time, and those differed by about 57 seconds in 1990. That gap has since been closed. On 16 August 2026 commit eeb8a2d made the chart path convert UT to TT before computing positions and the retrograde flag, so production no longer carries the approximation this audit described as outstanding. The parity harness still passes UT directly, so re-running it unchanged would exercise the July path rather than today's.
The larger Jupiter and Saturn residuals sit in a different bucket. A slow planet has such a flat longitude curve near its station that the reference vertex fit is itself less sharp, which puts a floor under how precisely any comparison can resolve those stations. That is a statement about the uncertainty of the comparison, not a demonstration that the remaining residual is entirely fitting error and the flag exact. We have not established the split, and would rather say so.
Sources and reproduction
Two kinds of number appear above and they should not be read the same way. External reference measurements come from JPL Horizons. Engine-derived comparisons come from our own code and are only as good as it is.
- External reference: NASA JPL Horizons, DE441 ephemeris, OBSERVER table,
CENTER='500@399'(geocentric),QUANTITIES='31'(apparent ecliptic longitude of date, i.e. tropical). See the Horizons observer-table specification. Pulled directly from the Horizons API as raw text and parsed, not through a summarising fetch. - Station times are external. They were located from the Horizons rows themselves, by 30-minute sampling with a parabolic vertex fit at the turning point, and do not depend on our code.
- Frame-shift figures are engine-derived. They were obtained by bisecting a centred difference on our own longitudes, in each frame, and are only as good as that engine. The corrected true-ayanamsa figure is reproducible via
scripts/retrograde-frame-shift.ts, with the run output retained atdocs/evidence/retrograde-frame-shift.md. Written separately from the July harness, that script reproduces the July mean-ayanamsa figure of -33.79 min at its published 0.01-minute precision, which is what cross-validates it. Both records expose the figure rounded to two decimals, so that is agreement to that precision rather than bit-level equality. - Sweep window: Mercury, Venus, Mars, Jupiter and Saturn, daily, 1 November 1989 to 1 July 1990. The reference direction at each sample is a centred difference across the neighbouring daily rows.
- Engine revision: the audit was run 26 July 2026, before commit
eeb8a2d(16 August 2026, "mean-sidereal convention + UT->TT time split") landed both the UT-to-TT conversion and the move to the true ayanamsa. The tables reflect the July engine. - Ayanamsa drift (~50.3 arcseconds per year) is the precession rate used for the Lahiri ayanamsa here. The July frame table uses the mean ayanamsa; the shipped chart conversion uses the true ayanamsa (mean plus nutation in longitude).
Every external figure above is reproducible against Horizons by anyone who wants to check it, which is the point of publishing it. Our Kundli generator and every divisional chart on the site run on the corrected flag.