Per-row defensive sync for media_provider_assets rows that may have
missed a webhook. Called by Rindle.Workers.MuxSyncCoordinator (Phase 34
ships the cron coordinator; Phase 35 wires up webhook-driven sync).
Job Arguments
%{"provider_asset_id" => mux_asset_id}Behavior
- Fetch row by
provider_asset_id. - If the row is past the stuck threshold, transition to
:erroredwithlast_sync_error: "stuck in :<state> past threshold"and emit[:rindle, :provider, :sync, :stuck]. - Otherwise, call
Rindle.Streaming.Provider.Mux.get_asset/1and reconcile FSM/playback_ids. Emit[:rindle, :provider, :sync, :resolved]. - If Mux returns 404, transition to
:erroredwith reason"mux asset not found"and emit:resolved(the row IS now reconciled with reality — there is no asset to wait for).
Telemetry Contract
[:rindle, :provider, :sync, :resolved]— fires on every successfulget_asset/1call (whether or not a state change occurred).measurements: %{system_time} metadata: %{profile, provider, asset_id, provider_state, age_ms}[:rindle, :provider, :sync, :stuck]— fires when the row'supdated_atexceeds:provider_stuck_threshold_seconds(default 7200). Same metadata shape;provider_statereflects the row's final:erroredstate.
metadata.asset_id is the redacted last-4-char tag of the
provider_asset_id (security invariant 14, via
Rindle.Domain.MediaProviderAsset.redact_id/1).