oxedyne/daimond/dev/verify_governor.mjs
6.7 KiB, 1 run
created by r2519314175:439, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | // verify_governor.mjs — the spend governor's pure decision core. |
| 2 | // |
| 3 | // The governor watches the RATE of spend, not the total, so that a fan-out of |
| 4 | // agents cannot burn a week's credit in a blink. This test drives the pure |
| 5 | // core directly under Node (no browser, no stack): the learned baseline, the |
| 6 | // predictive batch estimate, the dispatch decision, the live rate, and the |
| 7 | // green/amber/tripped level. The wiring into the real page is checked |
| 8 | // separately by verify_governor_ui.mjs against the running app. |
| 9 | // |
| 10 | // Loaded by evaluating www/js/governor.js in a sandbox: the module guards its |
| 11 | // browser globals behind `typeof`, so with `window` passed as undefined only |
| 12 | // the pure core and the Node export run. This is robust to whatever module |
| 13 | // system the app's package.json declares. |
| 14 | import { readFileSync } from 'fs'; |
| 15 | |
| 16 | const src = readFileSync(new URL('../www/js/governor.js', import.meta.url), 'utf8'); |
| 17 | const mod = { exports: {} }; |
| 18 | // eslint-disable-next-line no-new-func |
| 19 | new Function('module', 'window', src)(mod, undefined); |
| 20 | const Gov = mod.exports; |
| 21 | const C = Gov._core; |
| 22 | |
| 23 | const ok = [], bad = []; |
| 24 | const check = (name, pass, detail) => { |
| 25 | (pass ? ok : bad).push(name); |
| 26 | console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : '')); |
| 27 | }; |
| 28 | const near = (a, b, eps = 1e-9) => Math.abs(a - b) < eps; |
| 29 | |
| 30 | // ── The learned baseline ─────────────────────────────────────────── |
| 31 | { |
| 32 | // Too few samples: the baseline is assumed, not measured, and uses the |
| 33 | // fallback worker cost. |
| 34 | const thin = C.baselineFrom([{ t: 1, u: 0.05 }, { t: 2, u: 0.05 }]); |
| 35 | check('thin history is not "learned"', thin.learned === false); |
| 36 | check('thin baseline falls back to the fallback worker cost', |
| 37 | near(thin.perTurnUsd, C.consts.FALLBACK_WORKER_USD), 'got ' + thin.perTurnUsd); |
| 38 | |
| 39 | // Enough samples: the per-turn figure is the median of the priced turns, |
| 40 | // and zero/negative costs are ignored. |
| 41 | const rich = C.baselineFrom([ |
| 42 | { t: 1, u: 0.10 }, { t: 2, u: 0.20 }, { t: 3, u: 0.30 }, |
| 43 | { t: 4, u: 0.40 }, { t: 5, u: 0.50 }, { t: 6, u: 0.60 }, |
| 44 | { t: 7, u: 0 }, { t: 8, u: -1 }, |
| 45 | ]); |
| 46 | check('rich history is "learned"', rich.learned === true); |
| 47 | check('baseline per-turn is the median of priced turns', |
| 48 | near(rich.perTurnUsd, 0.35), 'got ' + rich.perTurnUsd); |
| 49 | check('baseline rate is floored at the pennies floor', |
| 50 | rich.rateUsdMin >= C.consts.MIN_RATE_FLOOR_USD_MIN); |
| 51 | } |
| 52 | |
| 53 | // ── The predictive estimate ──────────────────────────────────────── |
| 54 | { |
| 55 | const base = { perTurnUsd: 0.10 }; |
| 56 | check('a batch costs N times a worker', near(C.estimateBatch(30, base), 3.0), 'got ' + C.estimateBatch(30, base)); |
| 57 | check('a zero batch costs nothing', C.estimateBatch(0, base) === 0); |
| 58 | check('a batch with no baseline uses the fallback', |
| 59 | near(C.estimateBatch(10, null), 10 * C.consts.FALLBACK_WORKER_USD)); |
| 60 | } |
| 61 | |
| 62 | // ── The auto budget ──────────────────────────────────────────────── |
| 63 | { |
| 64 | // A cheap normal turn: the budget is the floor, not a trivial figure. |
| 65 | check('auto budget is floored', near(C.autoBudget({ perTurnUsd: 0.02 }), C.consts.DEFAULT_BUDGET_USD)); |
| 66 | // An expensive turn: the budget scales with how the person works. |
| 67 | check('auto budget scales with a costly turn', |
| 68 | near(C.autoBudget({ perTurnUsd: 0.50 }), 0.50 * C.consts.BUDGET_TURN_MULTIPLE), |
| 69 | 'got ' + C.autoBudget({ perTurnUsd: 0.50 })); |
| 70 | } |
| 71 | |
| 72 | // ── The dispatch decision (the heart) ────────────────────────────── |
| 73 | { |
| 74 | const base = C.baselineFrom([]); // fresh account: fallback $0.08/worker, $1 budget |
| 75 | const budget = C.autoBudget(base); |
| 76 | |
| 77 | // A handful of agents on a fresh burst: silent. |
| 78 | const few = C.decideDispatch(3, base, 0, budget); |
| 79 | check('a few agents dispatch silently', few.needsConfirm === false, |
| 80 | 'predicted ' + few.predicted.toFixed(2) + ' vs budget ' + budget.toFixed(2)); |
| 81 | |
| 82 | // The "fifty agents in a blink" case: it must ask. |
| 83 | const many = C.decideDispatch(50, base, 0, budget); |
| 84 | check('a big fan-out asks first', many.needsConfirm === true, |
| 85 | 'predicted ' + many.predicted.toFixed(2)); |
| 86 | |
| 87 | // A burst already near its budget: even a small batch that crosses it asks. |
| 88 | const nearCap = C.decideDispatch(3, base, budget - 0.10, budget); |
| 89 | check('a batch that would cross an already-spent budget asks', nearCap.needsConfirm === true); |
| 90 | |
| 91 | // The same batch on a fresh budget passes — the pause is about the runaway, |
| 92 | // not about the batch in isolation. |
| 93 | const fresh = C.decideDispatch(3, base, 0, budget); |
| 94 | check('the same batch on a fresh budget passes', fresh.needsConfirm === false); |
| 95 | |
| 96 | // A user with an expensive normal is not nagged for their normal fan-out: |
| 97 | // the budget scaled with them. |
| 98 | const richBase = C.baselineFrom(Array.from({ length: 8 }, (_, i) => ({ t: i, u: 0.40 }))); |
| 99 | const richBudget = C.autoBudget(richBase); |
| 100 | const richFew = C.decideDispatch(4, richBase, 0, richBudget); |
| 101 | check('an expensive-normal user is not nagged for a normal fan-out', |
| 102 | richFew.needsConfirm === false, |
| 103 | 'predicted ' + richFew.predicted.toFixed(2) + ' vs budget ' + richBudget.toFixed(2)); |
| 104 | } |
| 105 | |
| 106 | // ── The live rate ────────────────────────────────────────────────── |
| 107 | { |
| 108 | const now = 1_000_000; |
| 109 | const obs = [ |
| 110 | { t: now - 90_000, u: 5.00 }, // outside the 60s window — ignored |
| 111 | { t: now - 30_000, u: 0.50 }, |
| 112 | { t: now - 10_000, u: 0.50 }, |
| 113 | ]; |
| 114 | // $1.00 in the last minute → $1.00/min. |
| 115 | check('rate is the last-minute spend per minute', |
| 116 | near(C.velocityFrom(obs, now, 60_000), 1.0), 'got ' + C.velocityFrom(obs, now, 60_000)); |
| 117 | check('an empty window is a zero rate', C.velocityFrom([], now, 60_000) === 0); |
| 118 | } |
| 119 | |
| 120 | // ── The level ────────────────────────────────────────────────────── |
| 121 | { |
| 122 | const base = { rateUsdMin: 0.50 }; // normal ~ $0.50/min |
| 123 | const budget = 5.0; |
| 124 | check('an ordinary rate is green', C.levelFor(0.40, base, 0.20, budget) === 'green'); |
| 125 | // Well above baseline and above the pennies floor. |
| 126 | check('a fast rate is amber', C.levelFor(3.0, base, 1.0, budget) === 'amber', |
| 127 | 'got ' + C.levelFor(3.0, base, 1.0, budget)); |
| 128 | // Fast but still pennies: never amber (no nagging over nothing). |
| 129 | check('a fast rate on pennies is still green', |
| 130 | C.levelFor(0.40, { rateUsdMin: 0.05 }, 0.10, budget) === 'green'); |
| 131 | // A burst that has run past its budget: tripped. |
| 132 | check('past the budget is tripped', C.levelFor(0.10, base, budget + 1, budget) === 'tripped'); |
| 133 | } |
| 134 | |
| 135 | console.log('\n' + ok.length + ' ok, ' + bad.length + ' failed'); |
| 136 | process.exit(bad.length ? 1 : 0); |