The token-unlock cliff calendar was a static unlock_calendar.json imported once,
frozen since 2026-07-13 — it went STALE for weeks silently because the ONLY
nightly caller of the DefiLlama refresh was dropped in the Phase-0b venue
consolidation (assets.py flagged this exact follow-up). This restores an
automated feed.
- adapters/data/unlock_calendar_live.py (reborn, CALENDAR-only): fetch_unlock_events
pulls DefiLlama emissions + CoinGecko id->ticker, keeps only BEARISH cliffs
(insiders/privateSale, unlockType=cliff) for the tradeable perp universe,
cliff_day = ts//86400 (same Unix-epoch-day unit as the warehouse panel, so a
cliff matches the panel day directly). Injectable HTTP for no-network tests.
The legacy-venue price-snapshot half is NOT recovered (dead; panel now comes
from the Bybit warehouse).
- application/unlock_calendar_refresh.py: refresh_unlock_calendar upserts into
unlock_events (idempotent on sym+cliff_day).
- FAIL-LOUD: an empty/failed pull raises UnlockRefreshError -> the weekly Dagster
asset turns RED, instead of the silent staleness that caused this. write_events
never wipes existing rows on [], so a failed week keeps the last-known-good
calendar.
- orchestration: weekly `unlock_calendar_refresh` asset on its OWN job/schedule
(Mondays 22:00 UTC) — deliberately off the nightly hot path (a ~350-protocol
pull is heavy; cliffs are months ahead so weekly suffices).
- CLI `fxhnt unlock-calendar-refresh` for manual triggers.
Verified end-to-end against LIVE DefiLlama with the real 851-symbol Bybit
universe: 6760 bearish-cliff events, 2435 future cliffs (incl. today/tomorrow) —
the frozen calendar was missing all recent ones. 6 unit tests (parse/filter,
cliff_day unit, fail-loud, idempotent upsert, no-wipe-on-fail). Binance guard
updated: unlock_calendar_live is back but legacy-venue-free (grep guard enforces
it); 31 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>