__tests__/playbook.test.js
Rama main · réplica de lectura del repositorio original.
/**
* What this account has learned about where leads are.
*
* Every run used to start from nothing: rediscovering that Facebook has a post search, retrying
* phrasings somebody tried last week, sweeping a group that has produced nothing in a month. A
* prompt cannot fix that — telling a model "you know Facebook" does not tell it that "programmeur
* gezocht" returns recruiters while "wie kan mij helpen met een webshop" returns customers.
*
* The rule this file exists to enforce: the score is EARNED, never claimed. Nothing here asks the
* agent how it did.
*/
import { describe, it, expect, beforeEach } from 'vitest';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
process.env.PROFILE_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'gb-pb-'));
const pb = await import('../src/playbook.js');
const search = (q) => ({ kind: 'search', what: q });
describe('keeping score', () => {
beforeEach(() => pb.forget('facebook'));
it('records what a place returned, not what anyone thought of it', () => {
pb.recordSweep('facebook', search('webshop bouwen'), { posts: 12, tooOld: 3 });
const e = pb.summary('facebook').places[0];
expect(e).toMatchObject({ what: 'webshop bouwen', posts: 12, tooOld: 3, sweeps: 1, leads: 0 });
});
it('credits a lead to the place it came from', () => {
pb.recordSweep('facebook', search('webshop bouwen'), { posts: 10 });
pb.recordLead('facebook', search('webshop bouwen'));
expect(pb.summary('facebook').places[0].leads).toBe(1);
});
it('accumulates across runs, which is the whole point', () => {
for (let i = 0; i < 3; i++) pb.recordSweep('facebook', search('x'), { posts: 5 });
expect(pb.summary('facebook').places[0]).toMatchObject({ sweeps: 3, posts: 15 });
});
it('will not credit a lead to a place it has never swept', () => {
expect(pb.recordLead('facebook', search('never seen'))).toBeNull();
});
it('ignores a place it cannot name', () => {
expect(pb.recordSweep('facebook', { kind: 'search', what: ' ' }, { posts: 3 })).toBeNull();
});
});
describe('deciding what is worth trying again', () => {
beforeEach(() => pb.forget('facebook'));
/* Writing somewhere off after one quiet afternoon is how you lose the group that produces a
customer a month. It takes real evidence. */
it('does not write a place off on thin evidence', () => {
pb.recordSweep('facebook', search('quiet'), { posts: 4 });
expect(pb.summary('facebook').places[0].dead).toBe(false);
});
it('writes one off after enough sweeps and enough posts with nothing to show', () => {
for (let i = 0; i < 4; i++) pb.recordSweep('facebook', search('dead end'), { posts: 20 });
expect(pb.summary('facebook').places[0].dead).toBe(true);
});
it('never writes off a place that has produced somebody', () => {
for (let i = 0; i < 9; i++) pb.recordSweep('facebook', search('slow but real'), { posts: 40 });
pb.recordLead('facebook', search('slow but real'));
expect(pb.summary('facebook').places[0].dead).toBe(false);
});
});
describe('what the agent is handed', () => {
beforeEach(() => pb.forget('facebook'));
it('is empty before anything has been learned, rather than pretending', () => {
expect(pb.asContext('facebook')).toBe('');
});
it('names what worked and what to stop trying', () => {
pb.recordSweep('facebook', search('goede webbouwer'), { posts: 8 });
pb.recordLead('facebook', search('goede webbouwer'));
for (let i = 0; i < 4; i++) pb.recordSweep('facebook', search('software development'), { posts: 30 });
const ctx = pb.asContext('facebook');
expect(ctx).toMatch(/WHAT HAS WORKED BEFORE/);
expect(ctx).toMatch(/goede webbouwer/);
expect(ctx).toMatch(/NEVER PRODUCED ANYONE/);
expect(ctx).toMatch(/software development/);
});
/* A search tried once that found two is a better bet for the next twenty minutes than one tried
twenty times that found three. Ordering by total would say the opposite. */
it('puts the best bet first, by rate rather than by total', () => {
pb.recordSweep('facebook', search('rare gem'), { posts: 5 });
pb.recordLead('facebook', search('rare gem'));
pb.recordLead('facebook', search('rare gem'));
for (let i = 0; i < 20; i++) pb.recordSweep('facebook', search('grind'), { posts: 50 });
for (let i = 0; i < 3; i++) pb.recordLead('facebook', search('grind'));
const ctx = pb.asContext('facebook');
expect(ctx.indexOf('rare gem')).toBeLessThan(ctx.indexOf('grind'));
});
/* An average alone cannot tell "nothing here yet" from "thirty posts and never a lead", and only
one of those deserves another try. */
it('keeps the raw counts, so the history is readable', () => {
pb.recordSweep('facebook', search('x'), { posts: 8 });
pb.recordLead('facebook', search('x'));
expect(pb.asContext('facebook')).toMatch(/1 lead\(s\) from 8 post\(s\) over 1 sweep\(s\)/);
});
});