Pipeline advice follows the main wallet's buyer count (what the contract pays on); card states the linked-positions rule; chatbot knows it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-15 12:26:02 -05:00
parent 560d758ed9
commit 8ca019a5b3
2 changed files with 8 additions and 6 deletions
+7 -5
View File
@@ -10,8 +10,9 @@ const path = require('path');
const db = require('./db');
let DATA_DIR = null, accounts = null, coach = null, chain = null;
// linked positions (Qualified Start) belong to the member: their buyers count toward the account, the same way
// My line chips and the badges count them (Jim's card showed 1 buyer while his badge said Circuit, 2026-09-15)
// linked positions (Qualified Start) belong to the member and their buyers count toward badges, but the contract
// pays level 2 and 3 to the MAIN wallet only when that wallet's own buyerCount reaches 2 and 5. The card shows both
// numbers; the stage and the advice follow the main wallet, because that is what gets paid (Marty's audit, 2026-09-15)
const posCache = new Map();
async function positionBuyers(email) {
let ids = []; try { ids = (await accounts.positions(email)).map(p => p.memberId).filter(Boolean); } catch (e) {}
@@ -23,7 +24,6 @@ async function positionBuyers(email) {
}
return { count: n, positions: ids.length };
}
function rungForTotal(rung, total) { if (rung < 3) return rung; return total === 0 ? 3 : total === 1 ? 4 : total < 5 ? 5 : 6; }
const DAY = 86400000;
// columns, in order. Prospects live in the first and last; directs land by coaching rung.
@@ -95,14 +95,16 @@ async function board(email) {
const n = notes[m.email] || {};
const pb = c && c.rung >= 3 ? await positionBuyers(m.email) : { count: 0, positions: 0 };
const buyersMain = c ? c.buyerCount || 0 : 0, buyersAll = buyersMain + pb.count;
const rung = c ? rungForTotal(c.rung, buyersAll) : 0;
const rung = c ? c.rung : 0;
const RR = coach.RUNGS[rung];
// when positions carry buyers the main wallet does not, say exactly what the contract needs
const gap = pb.count && rung >= 3 && rung < 6 ? ' The contract pays level ' + (buyersMain < 2 ? '2' : '3') + ' to their main wallet only when that wallet has ' + (buyersMain < 2 ? 2 : 5) + ' qualifying buyers of its own: ' + buyersMain + ' now, plus ' + pb.count + ' on linked positions that count for badges only.' : '';
const stage = PARKED.has(n.tag) && rung < 3 ? 'later' : RUNG_STAGE[rung] || 'joined';
cards.push({ key: m.email, kind: 'member', email: m.email, name: m.username ? '@' + m.username : (m.memberId ? 'member #' + m.memberId : 'member'),
memberId: m.memberId || 0, stage, rung, rungLabel: RR.label, since: m.created || now,
lastSeen: m.lastSeen || 0, quietDays: c ? c.quietDays : 0, stalled: !!(c && c.stalled && rung < 6),
buyers: buyersAll, buyersMain, buyersPositions: pb.count, positions: pb.positions, bought: !!(c && c.counted),
next: RR.next, say: RR.say.replace('{{name}}', m.username || 'there'),
next: RR.next + gap, say: RR.say.replace('{{name}}', m.username || 'there'),
note: n.note || '', followUp: n.followUp || null, tag: n.tag || '' });
}
const due = cards.filter(c => c.followUp && c.followUp <= eod.getTime()).sort((a, b) => a.followUp - b.followUp);