Files
martbost 6a7c5161ff Give the top of the ladder a real prize: Team Grants + Network Intelligence
Marty's read was right — the upper tiers felt flat, and the reason is that
they scaled QUANTITY, not KIND. L1->L2 was a category change (you couldn't
write, now you can). Everything above L4 was a multiplier on a capability
already owned at L2: sound more like you, measure it, more pages, five at
once. Meanwhile the price doubles each rung — Corona costs ~17x Culmen. No
batch button is worth 17x a voice profile.

The fix is a change of axis. Every tool from L1 to L8 was a "me" tool, but
past the first levels this business isn't about you: get two, help your two,
teach them to teach. So the top now operates on the ORGANISATION.

L7 Team Grants — hand Suite capacity down to anyone in your own org.
Deliberately from a separate pool that the level grants, NOT out of the
leader's own allowance: making someone choose between equipping their team
and using their own tools means nobody ever grants anything. Recipients are
verified in-org against the contract. Grants can lift a tool the recipient's
level hasn't unlocked — that's the point, not a loophole. Being helped is
made visible to the recipient, since half the value is knowing an upline
backed you.

L8 Network Intelligence — every team ad pooled and anonymised: which angles,
headlines, creative and formats actually earn their impressions. No member
alone has enough traffic to learn anything from display; together they do.
It can't be bought, and it compounds monthly with no further work. Holds two
lines: nothing identifies anyone, and nothing under 2,000 impressions gets a
reported rate — under-evidenced rows are counted and disclosed rather than
silently dropped. The written readout is hand-authored, not generated:
these are conclusions about money, and a model paraphrasing a table is
exactly where an invented number slips in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 12:26:39 -05:00

180 lines
7.0 KiB
JavaScript

// Circle Suite — Network Intelligence (Corona, L8).
//
// Every member running ads is generating an answer to the same question: which
// wording actually makes a stranger click. Until now each of them only saw
// their own slice, which on display inventory is far too small to learn from —
// a member with 5,000 impressions and 4 clicks knows nothing.
//
// Pooled across the whole team it becomes a real asset, and one that cannot be
// bought or copied: it is the team's own accumulated result. It also gets
// better every month without anyone building anything, which is exactly what
// the top of a ladder should feel like.
//
// Two rules it holds to:
// 1. NOTHING identifies a member. No ids, no names, no per-person numbers.
// This is a picture of what works, not a leaderboard of who is working.
// 2. It refuses to report anything that has not earned the right to be
// reported. Display click rates are tiny, so a row with 300 impressions
// behind it is noise. Under-evidenced rows are counted and disclosed, not
// quietly dropped, so the reader knows what was left out.
'use strict';
const fs = require('fs');
const path = require('path');
const suiteTraffic = require('./suite-traffic');
let DATA_DIR = null;
function init(opts) { DATA_DIR = opts.dataDir; }
const MIN_LEVEL = 8;
const MIN_SERVED = 2000; // per row, before we will report a rate
const MIN_ROWS = 2; // before we will compare rows at all
function readJson(f) { try { return JSON.parse(fs.readFileSync(path.join(DATA_DIR, f), 'utf8')); } catch (e) { return {}; } }
// Pull every campaign the team has run, from every month we still hold.
function allCampaigns() {
const led = readJson('traffic-grants.json');
const out = [];
Object.keys(led).forEach(function (mk) {
const byMember = led[mk] || {};
Object.keys(byMember).forEach(function (mid) {
(byMember[mid] || []).forEach(function (c) {
out.push({
adId: Number(c.adId), kind: c.kind || 'banner', size: c.size || '',
creative: c.creative || '', subject: c.subject || '',
target: c.target || '', month: mk
});
});
});
});
return out;
}
// The angle is encoded in the destination (?v=pocket etc). Personal pages carry
// no angle, so they group separately rather than being lumped into "general".
function angleOf(target) {
const m = String(target || '').match(/[?&]v=([a-z]+)/i);
if (m) return m[1].toLowerCase();
if (String(target).indexOf('/p/') !== -1) return 'personal page';
return 'general';
}
function blankRow(label) {
return { label: label, served: 0, clicks: 0, ads: 0 };
}
function finish(map) {
const rows = Object.keys(map).map(function (k) {
const r = map[k];
r.rate = r.served > 0 ? r.clicks / r.served : 0;
r.enough = r.served >= MIN_SERVED;
return r;
});
const solid = rows.filter(function (r) { return r.enough; }).sort(function (a, b) { return b.rate - a.rate; });
const thin = rows.filter(function (r) { return !r.enough; });
return {
rows: solid,
thin: thin.length,
thinServed: thin.reduce(function (s, r) { return s + r.served; }, 0),
comparable: solid.length >= MIN_ROWS
};
}
async function report() {
const camps = allCampaigns();
if (!camps.length) return { ready: false, reason: 'No campaigns have run yet — this fills in as the team advertises.' };
// NAS caps a stats call, so batch.
const ids = camps.map(function (c) { return c.adId; }).filter(Boolean);
const stats = {};
for (let i = 0; i < ids.length; i += 150) {
try {
const chunk = await suiteTraffic.stats(ids.slice(i, i + 150));
chunk.forEach(function (s) { stats[s.ad_id] = s; });
} catch (e) { /* partial data still beats none */ }
}
if (!Object.keys(stats).length) {
return { ready: false, reason: 'The ad network did not return counters just now — try again shortly.' };
}
const byFormat = {}, bySize = {}, byAngle = {}, byCreative = {}, byHeadline = {};
let totalServed = 0, totalClicks = 0, counted = 0;
camps.forEach(function (c) {
const s = stats[c.adId];
if (!s) return;
const served = Math.max(0, Number(s.served) || 0);
const clicks = Math.max(0, Number(s.hits) || 0);
if (served <= 0) return;
counted++;
totalServed += served; totalClicks += clicks;
const add = function (map, key, label) {
if (!key) return;
if (!map[key]) map[key] = blankRow(label || key);
map[key].served += served; map[key].clicks += clicks; map[key].ads++;
};
add(byFormat, c.kind === 'text' ? 'text' : 'banner', c.kind === 'text' ? 'Text ads' : 'Banner ads');
add(byAngle, angleOf(c.target));
if (c.kind === 'text') {
if (c.subject) add(byHeadline, c.subject.toLowerCase(), c.subject);
} else {
if (c.size) add(bySize, c.size);
if (c.creative) add(byCreative, c.creative);
}
});
return {
ready: true,
totals: {
ads: counted, served: totalServed, clicks: totalClicks,
rate: totalServed > 0 ? totalClicks / totalServed : 0,
months: Object.keys(readJson('traffic-grants.json')).length
},
format: finish(byFormat),
angle: finish(byAngle),
size: finish(bySize),
creative: finish(byCreative),
headline: finish(byHeadline),
minServed: MIN_SERVED
};
}
// One plain-English read of the numbers. Deliberately written here rather than
// generated: these are conclusions about money, and a model paraphrasing a
// table is exactly where an invented number would slip in.
function readout(rep) {
if (!rep || !rep.ready) return [];
const out = [];
const pct = function (r) { return (r * 100).toFixed(3) + '%'; };
out.push('Across ' + rep.totals.ads.toLocaleString() + ' team ads: ' +
rep.totals.served.toLocaleString() + ' impressions served, ' +
rep.totals.clicks.toLocaleString() + ' clicks — ' + pct(rep.totals.rate) + ' overall.');
if (rep.format.comparable) {
const top = rep.format.rows[0], next = rep.format.rows[1];
const edge = next.rate > 0 ? ((top.rate - next.rate) / next.rate) * 100 : 0;
out.push(top.label + ' are pulling ' + pct(top.rate) + ' against ' + pct(next.rate) + ' for ' +
next.label.toLowerCase() + (edge >= 20 ? ' — a ' + Math.round(edge) + '% edge worth acting on.' : ' — close enough to call it a tie.'));
}
if (rep.angle.comparable) {
const a = rep.angle.rows[0];
out.push('The "' + a.label + '" angle is the strongest performer at ' + pct(a.rate) +
' across ' + a.served.toLocaleString() + ' impressions.');
}
if (rep.creative.comparable) {
const c = rep.creative.rows[0];
out.push('Best banner: ' + c.label + ' at ' + pct(c.rate) + '.');
}
const thinTotal = rep.format.thin + rep.angle.thin + rep.size.thin + rep.creative.thin + rep.headline.thin;
if (thinTotal) {
out.push(thinTotal + ' grouping(s) are held back for now — under ' + rep.minServed.toLocaleString() +
' impressions each, which is not enough to read anything into.');
}
return out;
}
module.exports = { init, report, readout, MIN_LEVEL, MIN_SERVED };