Skip to Content
Events and Indexing

Events and Indexing

Every state change in ClaimRush emits a Solidity event. These events are the primary data source for subgraphs, analytics dashboards (Dune), push notifications, and achievement systems. This page lists the canonical event tables by contract and the tooling you need to index them.

Canonical sources

WhatWhere
Event signaturessrc/lib/Events.sol (plus contract-local events like DelegationHub.SessionSet)
Decoding rulesdocs/analytics/dune-integration-pack-v1.0.0.md
ABIsabis/<network>/*.abi.json
Addressesdeployments/<network>.json
Runtime watcher setservices/event-watcher/src/abi.ts (canonical set of events actually watched in production; see docs/spec/real-time-layer-spec-v1.0.0.md)

Rule: Filter logs by evt_block_number >= startBlock.

Event codebooks (immutable)

EnumValues
ShareholderClaim.mode0=ETH, 1=LOCK_FURNACE
AutoCompoundPaused.reasonCode1=NOT_OWNER, 2=LISTED, 3=EXPIRED, 4=INVALID_TOKEN_ID, 5=FURNACE_REVERT, 6=QUOTE_FAILED, 7=CHECKPOINT_FAILED (also used by ShareholderAutoCompoundPaused and KingAutoLockSkipped)
FurnaceEnter.mode0=ENTER_WITH_ETH, 1=ENTER_WITH_CLAIM, 2=LOCK_FURNACE, 3=ENTER_WITH_TOKEN, 4=EXTEND_WITH_BONUS
LockDelisted.reason0=NORMAL, 1=EMERGENCY, 2=SOLD_INTO_OFFER (reserved; not emitted), 3=SOLD_TO_FURNACE, 4=EXPIRED, 5=APPROVAL_REVOKED (reserved; not emitted)
DelegationSessionUsed.actionTypeId1=TAKEOVER_FOR; 2=MINECORE_SET_REIGN_RECIPIENTS (coarse actionType enum: REIGN_RECIPIENTS); 10-12=CLAIM; 20-22=FURNACE_ENTER; 30-32=VE_LOCK; 40-42=CONFIG (see docs/manuals/developer/delegationhub.md)

Key event groups

This list is intentionally not exhaustive. It highlights the events most commonly consumed by UIs/indexers; treat src/lib/Events.sol as the source of truth.

MineCore: EntryTokenRegistrySet, FurnaceChanged, Takeover, ReignRecipientsSet, ReignFinalized, TakeoversPausedChanged, KingWithdrawal (+ KingWithdrawalTo), KingEthCredited, KingEthPaid, PendingClaimWithdrawn, Refund* (RefundCredited, RefundWithdrawn), ShareholderRoyaltiesTakeoverFailed, ShareholderRoyaltiesFlushFailed, FurnaceCreditReserveFailed, KingAutoLock*

DelegationHub / Delegation: SessionSet, DelegationSessionUsed

VeClaimNFT: FurnaceChanged, MineMarketChanged, LockCreated, LockExtended, LockAmountIncreased, LockMerged, LockUnlocked, AutoMaxSet, Transfer (burn/transfer tracking), DelegationSessionUsed, SlopeDriftClamped, ShareholderCheckpointFailed

ShareholderRoyalties: ShareholderWiringSet, ShareholderTakeoverAllocation, ShareholderFlush, ShareholderClaim (emitted on every ETH/lock claim), ShareholderAutoCompound*, RewardCheckpointCapReached, OverflowCheckpointCapReached

Furnace: MineCoreChanged, MineMarketChanged, ShareholderRoyaltiesChanged, FurnaceEnter, AutoMaxBonusClaimed, BonusPaid†, LpOverflowDripPaid†, LockSoldToFurnace†, LpStreamFunded, ReserveCredited, ReserveClamped, FurnaceQuoterSet, LpRewardsVaultSet, LpRewardsNotifyFailed, LockingPausedChanged

BonusPaid, LpOverflowDripPaid, and LockSoldToFurnace are emitted from the Furnace address via delegatecall into FurnaceGuardHelper (EIP-170 bytecode relief). The events are declared in IFurnace so they appear in Furnace’s compiled ABI and can be decoded by block explorers and indexers without merging a secondary ABI.

LpStakingVault7D: LpStaked, LpUnbondStarted, LpUnbondWithdrawn, LpRewardsNotified, LpRewardsClaimed, LpRewardsLocked, HarvestKeeperSet, AutoCompound*, LpFeesHarvestedToRewards, DelegationSessionUsed

MarketRouter: LockListed (limit sell / Market listing: minClaimOut + expiresAtTime), LockDelisted, ListingSettled, MarketSellToFurnace, SettlementKeeperSet, BonusTargetEscrowParamsChanged, TradingPausedChanged, BonusTargetEscrow* (limit buy / Buy intent: Created, Configured, Executed, AutoFurnaceExecuted, Expired, Cancelled, ExpiryExtended)

MaintenanceHub: Poked (includes checkpointOk and flushOk boolean flags indicating whether the ve checkpoint and shareholder flush succeeded individually), TokenRescued

Genesis: GenesisFinalized, SkimFailed (bounded revert data from failed pool skim), Locked, LockExtended, WithdrawLp (to is indexed), TokenRescued

Decoding notes

  • Reign recipients: ReignRecipientsSet(reignId, king, ethRecipient, claimRecipient) can be emitted mid-reign. king is the active reign identity whose routing changed, not the transaction caller. Use it to track where the dethroned-King 75% ETH payout and the King-stream CLAIM are routed.
  • Auto-compound pause reasons: the reasonCode codebook is shared across AutoCompoundPaused, ShareholderAutoCompoundPaused, and MineCore’s KingAutoLockSkipped.
  • MineCore king withdrawals: KingWithdrawal(user, amount) is always emitted. KingWithdrawalTo(user, to, amount) is emitted only when ETH was successfully delivered to to (not on fallback to user). If to rejects ETH and differs from user, MineCore retries delivery to user; in that case only KingWithdrawal fires.
  • King ETH payout success: KingEthPaid(recipient, amount) fires when the dethroned-King ETH payout push succeeds during a takeover.
  • King ETH payout fallback: KingEthCredited(recipient, amount) fires when the best-effort dethroned-King payout push (bounded gas stipend) fails during a takeover. The ETH is credited to kingEthBalance[recipient] for pull withdrawal via withdrawKingBalance().
  • Pending CLAIM withdrawal: PendingClaimWithdrawn(user, to, amount) fires when a user (or ClaimAllHelper on their behalf) withdraws pending CLAIM from pendingClaimBalance (credited when the auto-lock path skips or fails during a reign settlement).
  • Furnace reserve credit failure: FurnaceCreditReserveFailed(furnace, amount, reason) fires when Furnace.creditReserve(amount) reverts during a takeover. MineCore has already minted and transferred the CLAIM to the Furnace address, but the internal reserve accounting update failed. The reason field contains bounded revert data. Treat this as an operational alert — the Furnace holds the CLAIM but furnaceReserve is understated until a subsequent _syncFurnaceReserve() call corrects it.
  • LP Harvest & Lock: LpRewardsLocked.tokenId is the actual destination lock id returned by Furnace.enterWithClaimFor. If a new lock is minted, this is the minted token id (not the quote placeholder 0).
  • LP auto-compound heuristic: LpRewardsLockedEvent.autoCompounded in the subgraph is derived from the top-level tx.input selector (matching compoundFor/compoundForMany). If the auto-compound is routed through an intermediary contract (e.g., MaintenanceHub or a future batching proxy), the heuristic returns false because the subgraph cannot see internal calldata. Consumers that need reliable auto-compound attribution should cross-reference with the executor address or the upstream AutoCompoundConfigured state.
  • Reserved delist reasons: SOLD_INTO_OFFER (2) and APPROVAL_REVOKED (5) exist in the analytics codebook but the strict-mode router does not emit them. Production indexers should expect live LockDelisted.reason values of NORMAL, EMERGENCY, SOLD_TO_FURNACE, and EXPIRED.
  • Royalties hardening: If ShareholderRoyalties errors during takeover, MineCore emits:
    • ShareholderRoyaltiesTakeoverFailed(reignId, amountEth, reason) when onTakeover reverts (ETH is held in MineCore.shareholderEthPending for retry)
    • the same ShareholderRoyaltiesTakeoverFailed(...) event is also emitted if retryPushShareholderEth() later fails; in that retry path reignId = 0 and amountEth is the retried pending bucket
    • ShareholderRoyaltiesFlushFailed(reason) when flushPendingShareholderETH reverts (ETH remains in ShareholderRoyalties.pendingShareholderETH for later flush)
    • reason contains bounded revert data (up to 128 bytes) via _boundedRevertData(). Indexers can decode standard revert selectors from this field.
  • Baron auto-compound failures: ShareholderAutoCompoundFailed(user, amountEth, tokenId) fires when the downstream Furnace.lockEthReward(...) call reverts during auto-compound, or when the quote call fails in compoundForMany. The user’s ETH remains in ShareholderRoyalties (their claimableEth balance is unchanged). For Furnace reverts, ShareholderAutoCompoundPaused is also emitted with a reason code indicating the failure type. For quote failures in the batch path (compoundForMany), the user is skipped (not paused), so retry is automatic on the next cadence. Keepers should surface ShareholderAutoCompoundFailed events for operator alerting.
  • King auto-lock failures: KingAutoLockFailed.revertData is not generic downstream revert data. MineCore emits selector-encoded Errors.ZeroAddress / Errors.WiringMismatch for pre-call fail-closed cases. If the downstream Furnace call itself reverts, revertData contains bounded revert data (up to 128 bytes) via _boundedRevertData().
  • Furnace LP stream: BonusPaid.lpTopupClaim, LpOverflowDripPaid.dripAmount, and sellback lpReward all re-fund the stream schedule. Each re-fund emits LpStreamFunded(amountFunded, newRatePerSec, newPeriodFinish). Stream accrual later transfers CLAIM to the LP vault and attempts notifyRewards(...); successful notifies emit LpRewardsNotified, while failed notifies emit LpRewardsNotifyFailed(vault, amountClaim, revertData). In v1.0.0 that revertData field is emitted as empty bytes for hardening, and the vault can later reconcile the same CLAIM via balance-delta on a successful notify or fee harvest. The subgraph persists immutable LpStreamFundedEvent rows and mirrors the latest schedule onto FurnaceState.lpStreamRatePerSec / FurnaceState.lpStreamPeriodFinish.
  • enterWithToken: FurnaceEnter doesn’t include tokenIn/amountIn; recover from calldata or ERC20 Transfer logs.
  • EntryTokenRegistry: MineCore and Furnace emit EntryTokenRegistrySet independently.
  • Planned events (not yet declared): MarketLockRetargeted and MarketLockAbsorbed are reserved names for future MineMarket lock retargeting/absorption functionality. They are not currently declared in src/lib/Events.sol, not emitted by any contract, not present in shipped ABIs, and not indexed by the subgraph. No indexer or frontend action is needed until the corresponding contract logic ships. When these events do ship, they must be added to Events.sol, exported in the ABI, and wired into subgraph handlers; track in the MineMarket implementation checklist.
  • Protocol wiring singleton: Protocol.mineCore, Protocol.marketRouter, Protocol.furnace, and Protocol.shareholderRoyalties represent the latest observed current wiring inside the indexed event surface, not one-time seeds. Canonical update receipts are MineCore.FurnaceChanged, VeClaimNFT.FurnaceChanged, VeClaimNFT.MineMarketChanged, Furnace.MineCoreChanged, Furnace.MineMarketChanged, Furnace.ShareholderRoyaltiesChanged, and ShareholderRoyalties.ShareholderWiringSet. Note: DelegationHub, ClaimAllHelper, DexAdapter, ClaimToken, AgentLens, FurnaceQuoter, and MineCoreQuoter have no wiring-update events indexed (DexAdapter, ClaimToken, FurnaceQuoter, MineCoreQuoter, and AgentLens are not subgraph datasources at all).
  • ve snapshot semantics: User.veBalanceWei and VeLock.currentVeWei in the shipped subgraph are event-driven snapshots. They do not continuously decay between unrelated events. Frontend/API consumers that need “ve now” MUST recompute from amountWei, lockEnd, and autoMax against the same payload’s _meta.block.timestamp when available, or use the derived leaderboard/snapshot job. For per-user surfaces, that lock list MUST be the full owner snapshot, fetched via paginated tokenId cursor reads at one pinned head, not a capped first: N owner query. Wall-clock fallback is acceptable only when _meta is unavailable.
  • Delegation sessions: DelegationSessionUsed.actionTypeId is a numeric code (not an enum). refId meaning depends on the action. See docs/manuals/developer/delegationhub.md. Raw actionTypeId is canonical; the shipped coarse subgraph enum in subgraph/src/utils/delegation.ts maps actionTypeId = 2 (MINECORE_SET_REIGN_RECIPIENTS) to REIGN_RECIPIENTS. Security-session UIs MUST derive active vs expired from the same payload’s _meta.block.timestamp, not wall clock. A session is active iff perms > 0 and expiry >= _meta.block.timestamp; expiry = 0 is immediately expired, not active. Radar-style delegation/reign-recipient alert polling MUST page forward from a pinned (blockNumber,id) cursor at one _meta head or fail closed; a capped first: N recent-events query plus local seen-ID diff is a correctness bug because it can silently miss alerts.
  • Listing state: veNFTs do not emit dedicated “listed/frozen” events. Use MarketRouter events (LockListed, LockDelisted, ListingSettled) to index listing lifecycle.
  • Auto-furnace execution receipts: executeAutoFurnace emits both BonusTargetEscrowExecuted and BonusTargetEscrowAutoFurnaceExecuted in the same tx. Treat the generic BonusTargetEscrowExecuted receipt as canonical execution accounting; the auto-furnace-specific receipt is a detail companion. The shipped subgraph intentionally emits the ActivityItem only from the BonusTargetEscrowAutoFurnaceExecuted handler to avoid duplicate activity rows. If a future execution path emits only the generic receipt (without the companion), activity feed consumers would silently miss the event.
  • Offer history rows: the subgraph writes BonusTargetEscrowEvent.kind = FILLED from the generic execution receipt. AUTO_FURNACE_EXECUTED is retained as the same-tx companion row for detail consumers, so history UIs SHOULD filter or dedupe companion rows to avoid showing one fill twice.
  • Destination lock fallback: DestinationLockIneligible(offerId, destinationLockId) fires during executeAutoFurnace when the buyer’s requested destination lock is no longer eligible (expired, transferred, or delisted) and execution falls back to creating a new lock. Indexers should surface this for keeper/buyer alerting.

Indexing strategy

  • Use shipped ABIs as single decoding input; subgraph manifests MUST reference the exported abis/<network>/*.abi.json files directly rather than maintaining event-only copies under subgraph/abis/.
  • Prefer event-driven state (e.g., LockCreated/Extended/Unlocked for lifecycle)
  • Storage reads only for spot dashboard values
  • Follow docs/analytics/metrics-canon-v1.0.0.md for metrics
  • Leaderboards: docs/analytics/indexer-and-dune-implementation-guide-v1.0.0.md
  • Manifest drift checks: make subgraph-manifest-sync-check (or python3 scripts/sync_subgraph_manifest_from_deployments.py --check ...)
  • Runtime readiness checks: make subgraph-live-runtime-readiness-check (or python3 scripts/check_subgraph_manifest_runtime_readiness.py subgraph/subgraph.prod.yaml ...)
  • Frontend/API head-consistency checks: python3 scripts/check_frontend_subgraph_head_consistency.py
  • Owner-lock snapshot pagination checks: python3 scripts/check_owner_lock_snapshot_pagination.py
  • Delegation security polling/status checks: python3 scripts/check_delegation_security_polling.py
  • ABI coverage checks: python3 scripts/check_subgraph_events_vs_abi.py subgraph/subgraph.yaml ...
  • Mutable wiring semantic checks: python3 scripts/check_subgraph_protocol_wiring_semantics.py
  • Docs event-checklist parity checks: python3 scripts/check_subgraph_doc_event_checklist.py
  • Codegen/build layout checks: python3 scripts/check_subgraph_codegen_layout.py
  • Manifest entity completeness checks: python3 scripts/check_subgraph_manifest_entities.py subgraph/subgraph.yaml ...
  • Derived event-kind parity checks: python3 scripts/check_subgraph_derived_event_kinds.py
  • Subgraph query allowlist sync/check: make subgraph-query-allowlist-check (validation) and make subgraph-query-allowlist-sync (updates workers/chat/.subgraph-query-allowlist.env plus workers/chat/wrangler.jobs.toml prod/staging values); interpolated templates still need manual review
  • Local subgraph caveat: the local/default manifests (subgraph/subgraph.local.yaml and subgraph/subgraph.yaml when pointed at deployments/local.json) intentionally omit the Furnace block handler. On local graph-node setups, bonus quote snapshots only advance on Furnace bonus/reserve-affecting events, so idle-period quote history can look stale until the next relevant event.

Intentionally unindexed ABI events

The following ABI events are intentionally NOT handled by the subgraph because they are admin/ownership/approval lifecycle events that do not affect the product UI or analytics. Additionally, five ABI-exported contracts (AgentLens, ClaimToken, DexAdapter, FurnaceQuoter, MineCoreQuoter) are not subgraph datasources; their events are not indexed because those contracts either have zero events (AgentLens, FurnaceQuoter, MineCoreQuoter) or only emit admin/ownership events (DexAdapter) or wiring events already redundantly covered by other indexed contracts (ClaimToken.MineCoreChanged):

ConfigFrozen (ClaimToken, Furnace, MineCore, VeClaimNFT, ShareholderRoyalties), GuardianChanged (Furnace, MineCore, MarketRouter), OwnershipTransferStarted / OwnershipTransferred (7 contracts), Approval / ApprovalForAll (VeClaimNFT), EIP712DomainChanged (DelegationHub), ClaimAllHelperChanged (MineCore, ShareholderRoyalties), DelegationHubChanged (MineCore, Furnace), DustSwept (ShareholderRoyalties — owner-only sub-wei rounding dust recovery), RewardCheckpointCapReached / OverflowCheckpointCapReached (ShareholderRoyalties — operational monitoring for checkpoint array growth), EmergencyVaultRewireCancelled / EmergencyVaultRewireExecuted / EmergencyVaultRewireRequested (Furnace — guardian emergency LP vault rewire lifecycle; ops/audit trail only), DestinationLockIneligible (MarketRouter — swap routing rejects ineligible ve lock destination; no subgraph entity state), MetadataUpdate / BatchMetadataUpdate / BaseURISet / ContractURISet / ContractURIUpdated (VeClaimNFT — ERC-4906/ERC-7572 metadata signals for wallets and marketplaces; admin-only URI setters). These are documented in scripts/check_subgraph_events_vs_abi.py in the INTENTIONALLY_UNINDEXED_EVENTS allowlist and validated in CI. If any of these become product-relevant, add a manifest handler and remove from the allowlist.

Agent tooling

Repo tooling for offchain agents lives in agents/sdk/. It includes:

  • getGameStateSnapshot({ publicClient, manifest, ... }) (AgentLens-first, multicall fallback)
  • JSONL event streamer (RPC logs, optional subgraph backfill)
  • subgraph lag/core event-discoverable address parity checker

Run the streamer:

RPC_URL=... npm -C agents/sdk run example:events

Backfill recent history from the subgraph before live RPC logs:

RPC_URL=... SUBGRAPH_URL=... npm -C agents/sdk run example:events -- --backfill --backfill-limit 100

Validate subgraph health + core/event-discoverable address parity:

RPC_URL=... SUBGRAPH_URL=... npm -C agents/sdk run example:subgraph-health -- --pretty

Dune integration

ResourcePath
Event decoding + codebooksdocs/analytics/dune-integration-pack-v1.0.0.md
Metric meaningsdocs/analytics/metrics-canon-v1.0.0.md
Leaderboardsdocs/analytics/leaderboards-ui-and-dune-compatible-v1.0.0.md
SQL templatesanalytics/dune/

Usage: Get addresses from manifests → decode with ABIs → filter by startBlock → build from templates.

Achievements engine

Cosmetic badges computed offchain in chat worker.

Endpoint: GET /api/achievements?address=0x...&chainId=8453

Config (workers/chat): SUBGRAPH_URL (required), SUBGRAPH_AUTH_TOKEN (optional), ACHIEVEMENTS_SYNC_ENABLED, ACHIEVEMENTS_SYNC_OVERLAP_BLOCKS, ACHIEVEMENTS_SYNC_BATCH_SIZE

Required subgraph entities: Takeover, ReignFinalizedEvent, VeLock*Event, ShareholderClaimEvent, FurnaceEnterEvent, FurnaceState, LpStreamFundedEvent, MarketLockListedEvent, MarketSellToFurnaceEvent, BonusTargetEscrowEvent, BonusTargetEscrowExecutedEvent, BonusTargetEscrowAutoFurnaceExecutedEvent, LpStakedEvent, etc. See subgraph/schema.graphql.

Hard rule (background consumers): any capped subgraph history/event window MUST either paginate or fail closed. The shipped chat-worker consumers reject saturated windows for recent-touch scans, badge-profile history, Furnace percentile lookbacks, and push dispatch (RECENT_TOUCH_WINDOW_OVERFLOW, PROFILE_FOR_BADGES_WINDOW_OVERFLOW, FURNACE_PERCENTILE_WINDOW_OVERFLOW, PUSH_EVENT_WINDOW_OVERFLOW) so cursors and achievements/notifications cannot silently drift from truncated results. Push dispatch also MUST capture _meta first, pin its event query to that indexed head, and advance both last_dispatch_ts and last_unlock_scan_ts to the indexed head timestamp, never to wall clock.

Hard rule (timestamp-sorted feeds): if a frontend/API activity feed is ordered by timestamp desc, the cursor MUST carry both the boundary timestamp and a stable tie-breaker id. A raw id_lt filter is invalid for ActivityItem because ids are txHash/logIndex-derived and not monotonic by timestamp. The shipped /api/activity-feed path is guarded in CI by scripts/check_activity_feed_pagination.py.

Hard rule (owner lock sets): any frontend/API surface that derives current ve, active lock count, or lock eligibility from an owner’s lock set MUST fetch the full owner lock set via paginated tokenId cursor reads pinned to one _meta head. This applies both to direct veLocks(where: { owner: ... }) queries and to relation shortcuts like user(id: ...) { locks(first: N) }. A capped first: N owner-lock query is a correctness bug because it silently undercounts power users. The shipped frontend is guarded in CI by scripts/check_owner_lock_snapshot_pagination.py.

Hard rule (security session sets): any frontend/API surface that shows active delegation-session counts, active delegate cards, or bulk revoke targets MUST fetch the full delegationSessions set via paginated id cursor reads pinned to one _meta head. A capped delegationSessions(first: N) query is a correctness bug because it can hide active delegates and make revoke/security surfaces incomplete. The shipped frontend is guarded in CI by scripts/check_delegation_security_polling.py.

Hard rule (client-derived crown decay): any frontend surface that derives Crown takeover price, cost tier, or decay zone from subgraph data MUST use the same payload’s _meta.block.timestamp as “now”, including SSR/prefetched variants. A wall-clock-only derivation can drift ahead of the indexed head under lag and misstate urgency. The shipped Overview and Radar consumers are guarded in CI by scripts/check_frontend_subgraph_head_consistency.py.

Hard rule (multi-query analytics/admin surfaces): any dashboard or API route that stitches multiple event/entity windows into one payload MUST capture _meta first, pin every follow-up read to that same block, and bound every window to that same indexed timestamp. If _meta is unavailable, the consumer MUST fail closed instead of mixing wall-clock time with subgraph data. The shipped admin system metrics route is guarded in CI by scripts/check_admin_system_metrics_snapshot_consistency.py.

Hard rule (public Furnace windows): any frontend/API surface that reports Furnace 7d bonus payouts, 24h entry counts, or similar event windows MUST capture _meta first, pin the window scan to that block, and paginate with a stable cursor. skip pagination on a moving head is invalid for these public metrics because it can silently miss or double-count rows while the indexer advances. The shipped Furnace/Home consumers are guarded in CI by scripts/check_frontend_subgraph_head_consistency.py.

Hard rule (active market order sets): any surface that shows a user’s active listings or active offers MUST page the full active-order set at one pinned head. A capped marketListings(... first: N) or bonusTargetEscrows(... first: N) query silently undercounts active orders and can hide cancellable positions. Any listing/offer expiry or “expiring soon” urgency derived from that snapshot MUST also use the same payload’s _meta.block.timestamp as “now”; wall-clock-only expiry math can hide still-active indexed orders while the indexer lags. The shipped frontend is guarded in CI by scripts/check_market_order_snapshot_pagination.py.

See also

Public or shared API responses that derive live values from subgraph data must use the same payload’s _meta.block.timestamp, not wall clock fallback, for any time-sensitive calculations.

Range-cropped Crown APIs, reign recap/share payloads, and the live /crown/reign/[reignId] page must anchor their live nowSec and any since windows to a captured _meta head. For live mode, nowSec in /api/crown-reigns, /api/reign-recap, and the reign detail page must represent indexed time, not request wall clock time.