Following up on this - the root cause appears to be that s2Member is not using the [old_subscr_id] field that comes through with the first skipped payment IPN.
This was a relatively recent change by PayPal and has caught a lot of people out.
s2Member should update the Paid Subscr. ID field to the current [subscr_id] when it gets an IPN containing an [old_subscr_id], but it doesn't. Old numbers are prefixed S-, new ones are prefixed I-.
@clavaque are you able to look into this? Below I've included an example IPN containing the [old_subscr_id] field. You will also note from my first message that PayPal do not include this [old_subscr_id] field on the final recurring_payment_suspended_due_to_max_failed_payment IPN. The net result is that if the [subscr_id] is not updated after the first skipped payment IPN, the final failed IPN will not demote the user, because s2Member can't find them.
LOG ENTRY: Sun Jan 13th, 2019 @ precisely 3:16 pm UTC
PHP v5.6.39-0 :: WordPress v5.0.3 :: s2Member v170722 :: s2Member Pro v170722
Memory 31.44 MB :: Real Memory 32.52 MB :: Peak Memory 31.98 MB :: Real Peak Memory 32.52 MB
User-Agent: PayPal IPN ( https://www.paypal.com/ipn )
[txn_type] => subscr_failed
[subscr_id] => I-xxxxxxxxxxxx
[last_name] => xxxxx
[option_selection1] => nnn
[option_selection2] => xxx.xxx.xxx.xxx
[residence_country] => GB
[mc_currency] => GBP
[item_name] => Member - RENEW
[business] => firstname.lastname@example.org
[verify_sign] => x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[payer_status] => verified
[payer_email] => email@example.com
[first_name] => xxxxx
[receiver_email] => firstname.lastname@example.org
[option_name1] => Referencing Customer ID
[payer_id] => nxxxxxxxxnnxx
[invoice] => nxnxxnnnnnxn~xxx.xxx.xxx.xxx
[option_name2] => Customer IP Address
[retry_at] => 02:00:00 Jan 18, 2019 PST
[item_number] => 3
[mc_gross] => 80.00
[custom] => www.xxxxx.com
[old_subscr_id] => S-xxxxxxxxxxxxxxxxx
[charset] => windows-1252
[notify_version] => 3.9
[ipn_track_id] => nnxnnnxnnnxnx
[option_selection] => nnn
[option_name] => Referencing Customer ID
[s2member_log] => Array
 => IPN received on: Sun Jan 13, 2019 3:16:17 pm UTC
 => s2Member POST vars verified through a POST back to PayPal.
 => s2Member originating domain (`$_SERVER["HTTP_HOST"]`) validated.
 => s2Member `txn_type` identified as ( `subscr_failed|recurring_payment_failed|recurring_payment_skipped` ).
 => This `txn_type` does not require any action on the part of s2Member.
 => s2Member does NOT respond to individual failed payments, only multiple consecutive failed payments.
 => When multiple consecutive payments fail, a special IPN response will be triggered.
[subscr_gateway] => paypal
[subscr_baid] => I-xxxxxxxxxxxx
[subscr_cid] => I-xxxxxxxxxxxx