diff --git a/chain.js b/chain.js index e973757..f98fdb8 100644 --- a/chain.js +++ b/chain.js @@ -667,6 +667,13 @@ function getOrgShare(rootId) { }; } +// Live direct count from the index (null when unknown) — used by the rotation +// queue to reconcile against chain reality before activating a sponsor. +function liveDirects(id) { + if (!state || !state.members[id]) return null; + return state.members[id].directCount || 0; +} + // Wallet address -> position id (for wallet-verified messaging sign-in). // One position per address by contract design. function memberIdByAccount(address) { @@ -744,4 +751,4 @@ async function getIncome(id) { }; } -module.exports = { startIndexer, getPayoutsPublic, verifyMember, memberLookup, memberPublic, getIncome, getOwnerUpgradeNeeds, getOrgRouting, getOrgShare, getCoachingScan, getMatrixTree, isInTeam, nextOpenPosition, memberIdByAccount, balanceOf, CONTRACT }; +module.exports = { startIndexer, getPayoutsPublic, verifyMember, memberLookup, memberPublic, getIncome, getOwnerUpgradeNeeds, getOrgRouting, getOrgShare, getCoachingScan, getMatrixTree, isInTeam, nextOpenPosition, memberIdByAccount, liveDirects, balanceOf, CONTRACT }; diff --git a/server.js b/server.js index cbc0f11..01bbc66 100644 --- a/server.js +++ b/server.js @@ -316,6 +316,17 @@ function recordEvent(event, source) { writeJson(ANALYTICS_FILE, a); } function normalizeStatuses(sponsors, preferredActiveId=null) { + // Chain reconciliation: queue members can qualify through leg activity long + // BEFORE their rotation turn, and the event-driven auto-counter never sees + // it (bit us twice on 2026-08-19: #41, then #46 activated while already + // 2/2 on-chain). Never activate someone the chain says is qualified. + sponsors=sponsors.map(s=>{ + if(s.status==='qualified')return s; + const dc=chain.liveDirects(Number(s.id)); + if(dc!=null&&dc>=2)return {...s,directs:2,status:'qualified'}; + if(dc!=null&&dc>(Number(s.directs)||0))return {...s,directs:dc}; + return s; + }); const eligible=sponsors.filter(s=>s.status!=='qualified'); let activeId=preferredActiveId; if(!activeId || !eligible.some(s=>s.id===activeId)){