two taps, DIFFERENT cadences: exposure fired 27725 times, equity fired 27744 times
  -> lengths differ by 19 rows; zip-by-index would silently misalign every row past the first divergence

join_on_ts: spine(equity)=27744 rows -> 27744 JoinedRows (exactly one per spine entry)
exposure side: present(Some)=27725, absent(None)=19, by-ts cross-check mismatches=0

first 6 joined rows (equity spine | exposure side):
  ts=1420156800000 equity=0.0000 exposure=None (had not fired at this ts)
  ts=1420156919999 equity=0.0000 exposure=None (had not fired at this ts)
  ts=1420156980000 equity=0.0000 exposure=None (had not fired at this ts)
  ts=1420157040000 equity=0.0000 exposure=None (had not fired at this ts)
  ts=1420157099999 equity=0.0000 exposure=None (had not fired at this ts)
  ts=1420157160000 equity=0.0000 exposure=None (had not fired at this ts)

OK: two real-data taps at different cadences fused HONESTLY on timestamp (27725 aligned, 19 side-absent), never by index.
