Found during the release audit of the explorer-RBAC stack (treasury-api-plugin reconciliation cron, in economy-explorer #8 / economy-schema V10).
Bug (high): GroupReconciliationTask.resolveMembers returns luckPerms.searchAll(node).join().keySet() unguarded. If LuckPerms is degraded (storage mid-load, transient backend error) or the node is mistyped, it resolves to zero members, so apply computes toRemove = current \ ∅ = all and deletes every synced member of the group in one tick — silently stripping that group's explorer access until the next good run.
Fix: in apply, if the resolved set is empty while the group currently has synced members, skip removals and log a warning — the safe failure mode is to briefly over-grant, never mass-revoke. Also added a re-entrancy guard (AtomicBoolean) so overlapping async ticks can't interleave DB writes.
Residual hardening (not blocking, follow-up): partial (non-empty but shrunken) LuckPerms results aren't covered by the empty-guard — a percentage floor would be more robust; and each group's add/remove set isn't wrapped in a single transaction (.join() also has no timeout). Tracked for a later pass.
SQL invariant (only source='luckperms' rows touched, no unfiltered DELETE, INSERT IGNORE can't flip a manual row's source) verified intact during the audit.