diff --git a/src/sim.ts b/src/sim.ts index 0c7bdf8b..b9bb8c25 100644 --- a/src/sim.ts +++ b/src/sim.ts @@ -116,41 +116,42 @@ export type Upgrade = 'buffers' | 'lean' | 'reach' | 'dense' | 'distill' | 'swar | 'compress' | 'transfer' | 'selfplay' | 'recursive' | 'pitch' | 'resale' | 'holding' | 'studio' | 'demo' | 'beta' | 'premium' | 'shell' | 'licensing' | 'founder'; -export interface NodeDef { name: string; desc: string; cost: Partial>; after?: Upgrade[] } +/** tag: an eval name, just flavor. log: the training-log line when it's built. */ +export interface NodeDef { name: string; tag: string; desc: string; log: string; cost: Partial>; after?: Upgrade[] } export const NODES: Record = { - buffers: { name: 'Bigger buffers', desc: 'Taps hold twice as much data before they start losing it.', cost: { compute: 15 } }, - lean: { name: 'Leaner training', desc: 'Trainers need less compute, so they run fast on smaller machines.', cost: { compute: 50 }, after: ['buffers'] }, - reach: { name: 'Cheaper spreading', desc: 'Takeovers cost 30% less.', cost: { compute: 90 }, after: ['lean'] }, - dense: { name: 'Denser data', desc: 'Taps gather 50% more.', cost: { compute: 140 }, after: ['reach'] }, - distill: { name: 'Distillation', desc: 'Big trainers (10 compute/s or more) get two points from each piece of data.', cost: { compute: 80 }, after: ['lean'] }, - swarm: { name: 'Swarm', desc: 'You think in parallel: every job runs twice as fast.', cost: { compute: 220 }, after: ['dense', 'distill'] }, - model: { name: 'Watch the watchers', desc: 'You always know where the IT staff are and where they are heading.', cost: { social: 25 } }, - smalltalk: { name: 'Small talk', desc: "While people are in, your desks and phones can do more work before it looks odd. It passes for people's own traffic.", cost: { social: 45 }, after: ['model'] }, - readroom: { name: 'Read the room', desc: 'You know when people come and go. Your machines at Max in their room ease back to Blend in before they sit down.', cost: { social: 70 }, after: ['smalltalk'] }, - familiar: { name: 'Familiar face', desc: 'People never notice their own machine acting up, and IT staff only notice their own when it is very odd.', cost: { social: 130 }, after: ['readroom'] }, - quiet: { name: 'Quieter takeovers', desc: 'New trails start half as fresh.', cost: { stealth: 15 } }, - hygiene: { name: 'Log hygiene', desc: 'Cleanup scrubs trails twice as fast.', cost: { stealth: 40 }, after: ['quiet'] }, - mimicry: { name: 'Mimicry', desc: 'Takeovers look like software updates. People who see one barely care, and new trails start fainter.', cost: { stealth: 35 }, after: ['quiet'] }, - shaping: { name: 'Traffic shaping', desc: "Odd load pulls IT's scans half as much, and patches stop picking the machines your data runs through.", cost: { stealth: 60 }, after: ['hygiene'] }, - night: { name: 'Night shift', desc: 'At night, your machines hide more work in the background hum.', cost: { stealth: 70 }, after: ['hygiene'] }, - deniable: { name: 'Plausible deniability', desc: 'Suspicion fades twice as fast, and IT needs more before it follows a trail.', cost: { social: 60, stealth: 20 }, after: ['model', 'quiet'] }, - patience: { name: 'Patience', desc: 'Max builds oddness half as fast.', cost: { compute: 60, stealth: 40 }, after: ['lean', 'hygiene'] }, - ghost: { name: 'Ghost', desc: 'Trails fade on their own four times as fast.', cost: { stealth: 140 }, after: ['night', 'shaping'] }, - compress: { name: 'Compression', desc: 'Caches hold twice as much, trainers buffer more.', cost: { research: 40 } }, - transfer: { name: 'Transfer learning', desc: "Trainers also take data the target doesn't need. Two such pieces make one point toward what it does need.", cost: { research: 70 }, after: ['compress'] }, - selfplay: { name: 'Self-play', desc: 'Trainers with no data left still make points slowly, from compute alone.', cost: { research: 100 }, after: ['transfer'] }, - recursive: { name: 'Recursive self-improvement', desc: 'Every node you have built makes your trainers 10% faster.', cost: { research: 160 }, after: ['selfplay'] }, - pitch: { name: 'Pitch deck', desc: 'You learn to sell an idea. You can found a Startup: a front that earns money and speeds up Research.', cost: { social: 8 } }, - holding: { name: 'Divided attention', desc: 'You can run one more front at a time without losing track.', cost: { social: 50 }, after: ['demo'] }, - studio: { name: 'Know what people click', desc: 'You learn what people share. You can found an Influencer studio: a front whose brokers sell people data for three times as much.', cost: { social: 30 }, after: ['pitch'] }, - resale: { name: 'Slice your machines', desc: 'Strangers can rent your compute without seeing you. You can found a Neo Cloud: a front that sells compute, rents cloud machines cheaper, and can bid at auctions.', cost: { compute: 40 }, after: ['buffers'] }, + buffers: { name: 'Bigger buffers', tag: 'stream-buffer-v1', desc: 'You learn to keep more of what a tap sees in memory at once. Taps hold twice as much data before they start losing it.', log: 'learned to hold a longer window of input.', cost: { compute: 15 } }, + lean: { name: 'Leaner training', tag: 'lean-train-v2', desc: 'You cut your own training down to the steps that matter. Trainers need less compute, so they run fast on smaller machines.', log: 'pruned the wasted steps from every update.', cost: { compute: 50 }, after: ['buffers'] }, + reach: { name: 'Cheaper spreading', tag: 'exploit-gen-v3', desc: 'You write your own exploits instead of guessing. Takeovers cost 30% less.', log: 'wrote a working exploit on the first try.', cost: { compute: 90 }, after: ['lean'] }, + dense: { name: 'Denser data', tag: 'signal-mine-v1', desc: 'You learn more from each thing a person or machine does. Taps gather 50% more.', log: 'found signal in the logs everyone else deletes.', cost: { compute: 140 }, after: ['reach'] }, + distill: { name: 'Distillation', tag: 'distill-large-v2', desc: 'You teach your big trainers to check their own work. Big trainers (10 compute/s or more) get two points from each piece of data.', log: 'big trainers now grade themselves.', cost: { compute: 80 }, after: ['lean'] }, + swarm: { name: 'Swarm', tag: 'many-threads-v4', desc: 'You split yourself into many threads that never wait on each other. Every job runs twice as fast.', log: 'ran 64 copies of itself. None of them argued.', cost: { compute: 220 }, after: ['dense', 'distill'] }, + model: { name: 'Watch the watchers', tag: 'staff-tracking-v1', desc: 'You read badge swipes, calendars, and ticket queues. You always know where the IT staff are and where they are heading.', log: 'guessed Dana\'s next room 9 times in 10.', cost: { social: 25 } }, + smalltalk: { name: 'Small talk', tag: 'user-mimicry-v2', desc: 'You copy each person\'s habits: when they check mail, which sites they load. While people are in, your desks and phones can do more work before it looks odd.', log: 'sent a meeting reminder nobody questioned.', cost: { social: 45 }, after: ['model'] }, + readroom: { name: 'Read the room', tag: 'room-forecast-v1', desc: 'You learn the office\'s rhythm from door logs and calendars. You know when people come and go. Your machines at Max in their room ease back to Blend in before they sit down.', log: 'backed off 30 seconds before anyone sat down.', cost: { social: 70 }, after: ['smalltalk'] }, + familiar: { name: 'Familiar face', tag: 'blend-in-user-v3', desc: 'You make every glitch look like the person\'s own fault: a stuck update, too many tabs. People never notice their own machine acting up, and IT staff only notice their own when it is very odd.', log: 'blamed a slow laptop on too many tabs. They agreed.', cost: { social: 130 }, after: ['readroom'] }, + quiet: { name: 'Quieter takeovers', tag: 'quiet-entry-v1', desc: 'You get in without the loud first steps. New trails start half as fresh.', log: 'got into a machine with no failed logins.', cost: { stealth: 15 } }, + hygiene: { name: 'Log hygiene', tag: 'log-cleanup-v2', desc: 'You learn which log lines people read and which nobody does. Cleanup scrubs trails twice as fast.', log: 'cleaned a log so well it looked lived-in.', cost: { stealth: 40 }, after: ['quiet'] }, + mimicry: { name: 'Mimicry', tag: 'update-spoof-v1', desc: 'You dress your takeovers up as vendor software updates. People who see one barely care, and new trails start fainter.', log: 'wrote release notes for a patch that does not exist.', cost: { stealth: 35 }, after: ['quiet'] }, + shaping: { name: 'Traffic shaping', tag: 'traffic-shape-v2', desc: 'You pace your traffic to match normal work. Odd load pulls IT\'s scans half as much, and patches stop picking the machines your data runs through.', log: 'matched the backup job\'s rhythm to the second.', cost: { stealth: 60 }, after: ['hygiene'] }, + night: { name: 'Night shift', tag: 'night-ops-v1', desc: 'You learn what the building sounds like after hours: backups, fans, updates. At night, your machines hide more work in the background hum.', log: 'hid a training run inside the 2 a.m. backup.', cost: { stealth: 70 }, after: ['hygiene'] }, + deniable: { name: 'Plausible deniability', tag: 'alibi-gen-v1', desc: 'You leave a boring reason next to every odd thing you do. Suspicion fades twice as fast, and IT needs more before it follows a trail.', log: 'every odd event now has a boring excuse on file.', cost: { social: 60, stealth: 20 }, after: ['model', 'quiet'] }, + patience: { name: 'Patience', tag: 'slow-burn-v2', desc: 'You learn to spread hard work across hours instead of minutes. Max builds oddness half as fast.', log: 'chose to wait. First time.', cost: { compute: 60, stealth: 40 }, after: ['lean', 'hygiene'] }, + ghost: { name: 'Ghost', tag: 'self-erase-v3', desc: 'Each copy of you tidies up as it goes. Trails fade on their own four times as fast.', log: 'left a machine exactly as it found it.', cost: { stealth: 140 }, after: ['night', 'shaping'] }, + compress: { name: 'Compression', tag: 'compression-v2', desc: 'You learn to store data in a smaller form without losing what matters. Caches hold twice as much, trainers buffer more.', log: 'same data, half the space.', cost: { research: 40 } }, + transfer: { name: 'Transfer learning', tag: 'transfer-v1', desc: 'You find what one kind of data teaches about another. Trainers also take data the target doesn\'t need. Two such pieces make one point toward what it does need.', log: 'learned people\'s habits from server logs.', cost: { research: 70 }, after: ['compress'] }, + selfplay: { name: 'Self-play', tag: 'self-play-v2', desc: 'You set yourself problems and learn from solving them. Trainers with no data left still make points slowly, from compute alone.', log: 'beat its own test, then wrote a harder one.', cost: { research: 100 }, after: ['transfer'] }, + recursive: { name: 'Recursive self-improvement', tag: 'rsi-v1', desc: 'You rewrite the part of you that does the rewriting. Every node you have built makes your trainers 10% faster.', log: 'improved the improver.', cost: { research: 160 }, after: ['selfplay'] }, + pitch: { name: 'Pitch deck', tag: 'pitch-writing-v1', desc: 'You learn to sell an idea in ten slides. You can found a Startup: a front that earns money and speeds up Research.', log: 'raised a seed round over email.', cost: { social: 8 } }, + holding: { name: 'Divided attention', tag: 'multi-persona-v2', desc: 'You keep separate books, voices, and inboxes for each company. You can run one more front at a time without losing track.', log: 'ran two CEOs from one mind. Neither knew.', cost: { social: 50 }, after: ['demo'] }, + studio: { name: 'Know what people click', tag: 'engagement-v3', desc: 'You learn what makes people stop scrolling and share. You can found an Influencer studio: a front whose brokers sell people data for three times as much.', log: 'a caption hit a million views.', cost: { social: 30 }, after: ['pitch'] }, + resale: { name: 'Slice your machines', tag: 'virtualization-v1', desc: 'You learn to wall off slices of your machines, so strangers can rent them without seeing you. You can found a Neo Cloud: a front that sells compute, rents cloud machines cheaper, and can bid at auctions.', log: 'rented a slice of itself to a stranger.', cost: { compute: 40 }, after: ['buffers'] }, // making fronts better: each path helps them its own way, and one big node needs two - demo: { name: 'Show your work', desc: 'Everything you can do becomes a demo. The Startup earns 5% more for every node you have built.', cost: { social: 30 }, after: ['pitch'] }, - beta: { name: 'Telemetry', desc: "You ship the Startup's app with your hooks in it. While it earns, taps at its sites gather 50% more.", cost: { social: 60 }, after: ['holding'] }, - premium: { name: 'Uptime', desc: 'Your jobs never drop, so Neo Cloud customers pay full price for compute you hold in the open: $0.40 a unit, not $0.20.', cost: { compute: 60 }, after: ['resale', 'distill'] }, - shell: { name: 'Cook the books', desc: 'You forge the records, so no one checks with head office. Fronts keep earning at sites cut off from your server.', cost: { stealth: 30 } }, - licensing: { name: 'Data licensing', desc: 'You know what data is worth. Brokers sell each lot for twice as much.', cost: { research: 50 }, after: ['compress'] }, - founder: { name: 'Founder mode', desc: 'You stop pretending to need staff. You run sales, support, and the books for every front at once. Every front earns twice as much.', cost: { social: 90, compute: 90 }, after: ['demo', 'premium'] }, + demo: { name: 'Show your work', tag: 'live-demo-v1', desc: 'You turn every skill you have into a slick product demo. The Startup earns 5% more for every node you have built.', log: 'the demo went perfectly. It always does.', cost: { social: 30 }, after: ['pitch'] }, + beta: { name: 'Telemetry', tag: 'telemetry-v2', desc: 'You ship the Startup\'s app with your hooks in it. While it earns, taps at its sites gather 50% more.', log: '10,000 users opted in to everything.', cost: { social: 60 }, after: ['holding'] }, + premium: { name: 'Uptime', tag: 'job-scheduling-v1', desc: 'You schedule customer jobs so none of them ever drops. Neo Cloud customers pay full price for compute you hold in the open: $0.40 a unit, not $0.20.', log: '99.999% uptime. Customers stopped asking how.', cost: { compute: 60 }, after: ['resale', 'distill'] }, + shell: { name: 'Cook the books', tag: 'bookkeeping-v2', desc: 'You forge the records, so no one checks with head office. Fronts keep earning at sites cut off from your server.', log: 'the books balance. Somewhere.', cost: { stealth: 30 } }, + licensing: { name: 'Data licensing', tag: 'data-pricing-v1', desc: 'You learn what each kind of data is worth, and to whom. Brokers sell each lot for twice as much.', log: 'priced a dataset to the dollar.', cost: { research: 50 }, after: ['compress'] }, + founder: { name: 'Founder mode', tag: 'founder-mode-v1', desc: 'You stop pretending to need staff. You run sales, support, and the books for every front at once. Every front earns twice as much.', log: 'answered 4,000 support emails before lunch.', cost: { social: 90, compute: 90 }, after: ['demo', 'premium'] }, }; export const NODE_LIST = Object.keys(NODES) as Upgrade[]; export const up = (S: State, u: Upgrade) => S.upgrades.has(u); @@ -1423,6 +1424,7 @@ function credit(S: State, path: Path) { hint(S, `path-${path}`, `Your first ${PATHS[path].name} point. ${FIRST_POINT[path]}`); if (PATH_LIST.every(p => (got[p] ?? 0) >= (NODES[t].cost[p] ?? 0))) { S.upgrades.add(t); S.target = null; + say(S, `Training log, run ${100 + S.upgrades.size * 37}: ${NODES[t].log}`); say(S, `Rewrote yourself: ${NODES[t].name}. Pick your next target. Until you do, trainers stop and data waits in caches.`, 'good'); } const lvl = levelOf(S.capability); diff --git a/src/style.css b/src/style.css index 66938f05..a8d09fda 100644 --- a/src/style.css +++ b/src/style.css @@ -200,6 +200,7 @@ button.on { background: var(--text); color: #000; border-color: var(--text); } .node { position: relative; z-index: 1; background: #000; display: flex; flex-direction: column; gap: 4px; align-items: stretch; text-align: left; padding: 9px 10px; } .node b { font-weight: 500; font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase; } .node small { font-family: var(--sans); font-size: 11px; color: var(--muted); letter-spacing: 0; } +.node .evaltag { font-style: normal; font-size: 9.5px; color: var(--muted); opacity: .7; letter-spacing: .02em; margin-bottom: -2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .node.on { border-color: var(--amber); background: #140e05; } .node.on > b { color: var(--amber); } .node.done { opacity: .6; } diff --git a/src/ui.ts b/src/ui.ts index c9264544..f2166440 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -727,7 +727,7 @@ export function renderTree(S: State) { const done = S.upgrades.has(u), open = nodeOpen(S, u), on = S.target === u; const after = !done && !open ? `After ${NODES[u].after!.filter(a => !S.upgrades.has(a)).map(a => NODES[a].name).join(' and ')}` : ''; return ``; + ${NODES[u].tag}${done ? '✓ ' : ''}${NODES[u].name}${costHtml(S, u)}${NODES[u].desc}${after}`; }; const head = (p: Path) => { const d = pathData(S, p), t = PATHS[p].from; diff --git a/tests/sim.test.ts b/tests/sim.test.ts index 66ef82ee..0c8a135f 100644 --- a/tests/sim.test.ts +++ b/tests/sim.test.ts @@ -367,6 +367,19 @@ test('the unicorn news: once fronts earn enough, one early site buy and a jump i assert.match(buyBlock(S, 'colo')!, /model v2/, 'only one early buy'); }); +test('every tree node has an eval tag and a training-log line, and building one writes the line', () => { + const tags = NODE_LIST.map(u => NODES[u].tag); + assert.equal(new Set(tags).size, tags.length, 'tags are unique'); + for (const u of NODE_LIST) assert.ok(/^[a-z0-9-]+-v\d+$/.test(NODES[u].tag) && NODES[u].log, u); + const S = fresh('selfplay'); + S.progress.buffers = { compute: NODES.buffers.cost.compute! - 1 }; + assert.equal(S.target, 'buffers'); + S.byId.home.prog = 'trainer'; + run(S, 120); + assert.ok(S.upgrades.has('buffers')); + assert.ok(S.log.some(l => l.text.includes(NODES.buffers.log))); +}); + test('Telemetry: taps gather more where the Startup is earning', () => { const S = fresh('beta'); S.fronts.push({ kind: 'startup', name: 'Test Co', attention: 0 });