Classify rotation joins by worked-sponsor status, not just active

A rotation joiner submits their ID after the rotation may have advanced
(esp. with auto-advance: the direct who completes a sponsor's 2/2 joined
under it, but the queue has already moved on). Keying "rotation" off the
currently-active sponsor mislabeled them "leg" (hit live: Harvey #92
under #36 after #36 qualified). Now: rotation if the on-chain referrer
is a queue sponsor that is active OR qualified (was/is worked); a
still-waiting or non-queue referrer stays a leg join, preserving the
original #49/#56-under-#46 fix. Auto-enqueue now parents the new
position to the actual on-chain referrer, not the current active.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-15 05:43:03 -05:00
parent e1d6b782a2
commit f5de98c214
+15 -7
View File
@@ -156,12 +156,20 @@ async function handleSubmitId(req, res) {
new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), 6000)) new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), 6000))
]); ]);
} catch (e) { onchain = null; } } catch (e) { onchain = null; }
// the chain decides the path: rotation join (referrer = active rotation sponsor) // the chain decides the path: a rotation join is one whose on-chain referrer is
// vs leg join (someone's personal team build) — regardless of which page they used // a rotation-queue sponsor that IS or HAS BEEN worked (status active or
const active = activeSponsor(getSponsors()); // qualified) — not just the currently-active one. With auto-advance, the direct
// who completes a sponsor's 2/2 (and thus joined under it) submits their ID
// AFTER the rotation has already moved on, so keying off "active only" wrongly
// labeled them a leg join. A referrer that's a still-waiting queue position, or
// not in the queue at all, is a personal leg join.
const sponsorsNow = getSponsors();
const active = activeSponsor(sponsorsNow);
let joinPath = 'unknown'; let joinPath = 'unknown';
if (onchain && onchain.registered) joinPath = (active && String(onchain.referrerId) === String(active.id)) ? 'rotation' : 'leg'; if (onchain && onchain.registered) {
else if (onchain && !onchain.registered) joinPath = 'notfound'; const refSp = sponsorsNow.find(s => String(s.id) === String(onchain.referrerId));
joinPath = (refSp && (refSp.status === 'active' || refSp.status === 'qualified')) ? 'rotation' : 'leg';
} else if (onchain && !onchain.registered) joinPath = 'notfound';
subs.push({ newId, memberName, sponsorId, source: source||'(direct)', clickid, ts: new Date().toISOString(), path: joinPath, subs.push({ newId, memberName, sponsorId, source: source||'(direct)', clickid, ts: new Date().toISOString(), path: joinPath,
onchain: onchain ? { registered: onchain.registered, tier: onchain.tierName, level: onchain.levelName, referrerId: onchain.referrerId, uplineId: onchain.uplineId } : undefined }); onchain: onchain ? { registered: onchain.registered, tier: onchain.tierName, level: onchain.levelName, referrerId: onchain.referrerId, uplineId: onchain.uplineId } : undefined });
writeJson(SUBMISSIONS_FILE, subs.slice(-1000)); writeJson(SUBMISSIONS_FILE, subs.slice(-1000));
@@ -177,14 +185,14 @@ async function handleSubmitId(req, res) {
queueNote = 'Already in the rotation queue.'; queueNote = 'Already in the rotation queue.';
} else { } else {
const maxOrder = sponsors.reduce((m, s) => Math.max(m, s.sortOrder || 0), 0); const maxOrder = sponsors.reduce((m, s) => Math.max(m, s.sortOrder || 0), 0);
sponsors.push({ id: String(newId), name: memberName, parentId: String(active.id), directs: 0, level: onchain.levelName || 'Scintilla', status: sponsors.some(s => s.status === 'active') ? 'waiting' : 'active', sortOrder: maxOrder + 10, clicks: 0, notes: `auto-added: rotation join ${new Date().toISOString().slice(0, 10)}` }); sponsors.push({ id: String(newId), name: memberName, parentId: String(onchain.referrerId), directs: 0, level: onchain.levelName || 'Scintilla', status: sponsors.some(s => s.status === 'active') ? 'waiting' : 'active', sortOrder: maxOrder + 10, clicks: 0, notes: `auto-added: rotation join under #${onchain.referrerId} ${new Date().toISOString().slice(0, 10)}` });
sponsors = normalizeStatuses(sponsors); sponsors = normalizeStatuses(sponsors);
saveSponsors(sponsors); saveSponsors(sponsors);
const waitingAhead = sponsors.filter(s => s.status === 'waiting' && (s.sortOrder || 0) < maxOrder + 10).length; const waitingAhead = sponsors.filter(s => s.status === 'waiting' && (s.sortOrder || 0) < maxOrder + 10).length;
queueNote = `Auto-added to the rotation queue (${waitingAhead} waiting ahead of them).`; queueNote = `Auto-added to the rotation queue (${waitingAhead} waiting ahead of them).`;
} }
} catch (e) { queueNote = `⚠ Auto-add to queue failed (${e.message}) — add manually.`; console.error('queue auto-add', e.message); } } catch (e) { queueNote = `⚠ Auto-add to queue failed (${e.message}) — add manually.`; console.error('queue auto-add', e.message); }
msg = `🔔 RM Circle: ROTATION JOIN CONFIRMED ✅\nName: ${memberName}\nNew ID: ${newId} (${onchain.tierName}, verified on-chain)\nJoined under rotation sponsor: #${onchain.referrerId}\nSource: ${source||'(direct)'}\n✅ ${queueNote}\n(+1 direct for ID ${active.id} syncs from the chain automatically.)`; msg = `🔔 RM Circle: ROTATION JOIN CONFIRMED ✅\nName: ${memberName}\nNew ID: ${newId} (${onchain.tierName}, verified on-chain)\nJoined under rotation sponsor: #${onchain.referrerId}\nSource: ${source||'(direct)'}\n✅ ${queueNote}\n(Sponsor #${onchain.referrerId}'s direct count syncs from the chain automatically.)`;
} else if (joinPath === 'leg') { } else if (joinPath === 'leg') {
msg = `🌱 RM Circle: TEAM-BUILD JOIN (not rotation)\nName: ${memberName}\nNew ID: ${newId} (${onchain.tierName}, verified on-chain)\nActual sponsor on-chain: #${onchain.referrerId}${sponsorId!=='?'&&String(onchain.referrerId)!==sponsorId?` (form said ${sponsorId})`:''}\nSource: ${source||'(direct)'}\n→ Leg growth under #${onchain.referrerId} — no rotation action needed. Add them to the rotation queue only if they want the team effort.`; msg = `🌱 RM Circle: TEAM-BUILD JOIN (not rotation)\nName: ${memberName}\nNew ID: ${newId} (${onchain.tierName}, verified on-chain)\nActual sponsor on-chain: #${onchain.referrerId}${sponsorId!=='?'&&String(onchain.referrerId)!==sponsorId?` (form said ${sponsorId})`:''}\nSource: ${source||'(direct)'}\n→ Leg growth under #${onchain.referrerId} — no rotation action needed. Add them to the rotation queue only if they want the team effort.`;
} else if (joinPath === 'notfound') { } else if (joinPath === 'notfound') {