oxedyne/daimond/dev/verify_governor_ui.mjs
3.4 KiB, 1 run
created by r2519314175:441, 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_ui.mjs — the spend governor, wired into the real page. |
| 2 | // |
| 3 | // The pure decision core is covered by verify_governor.mjs under Node. This |
| 4 | // drives the actual app (governor.js loaded in index.html, alongside the real |
| 5 | // ledger) to prove the wiring: the module is present, its live decision runs, |
| 6 | // its state escalates to amber after a burst, and no console error is thrown. |
| 7 | // |
| 8 | // Needs dev/serve.mjs (DAIMOND_PORT, default 8777) and dev/mockllm.mjs |
| 9 | // (DAIMOND_MOCK_PORT, default 9099). The gateway is NOT needed — the governor never |
| 10 | // touches it. |
| 11 | import { open, shot } from './harness.mjs'; |
| 12 | |
| 13 | const ok = [], bad = []; |
| 14 | const check = (name, pass, detail) => { |
| 15 | (pass ? ok : bad).push(name); |
| 16 | console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : '')); |
| 17 | }; |
| 18 | |
| 19 | const s = await open({ name: 'governor', signIn: true, connect: true }); |
| 20 | const { page } = s; |
| 21 | |
| 22 | // 1. The module and the ledger getter it depends on are both present. |
| 23 | const present = await page.evaluate(() => ({ |
| 24 | gov: !!window.DaimondGovernor, |
| 25 | samples: !!(window.DaimondLedger && typeof window.DaimondLedger.samples === 'function'), |
| 26 | assess: !!(window.DaimondGovernor && typeof window.DaimondGovernor.assessDispatch === 'function'), |
| 27 | })); |
| 28 | check('DaimondGovernor is loaded', present.gov); |
| 29 | check('DaimondLedger.samples exists', present.samples); |
| 30 | check('assessDispatch is exposed', present.assess); |
| 31 | |
| 32 | // 2. The live decision: a normal fan-out clears, a big one asks. This runs the |
| 33 | // real module against the real (empty) ledger, so it exercises the fallback |
| 34 | // baseline and the auto budget as they ship. |
| 35 | const decision = await page.evaluate(() => ({ |
| 36 | few: window.DaimondGovernor.assessDispatch(3), |
| 37 | many: window.DaimondGovernor.assessDispatch(60), |
| 38 | })); |
| 39 | check('a few agents clear silently in-page', decision.few && decision.few.needsConfirm === false, |
| 40 | 'predicted ' + (decision.few && decision.few.predicted)); |
| 41 | check('a big fan-out needs confirming in-page', decision.many && decision.many.needsConfirm === true, |
| 42 | 'predicted ' + (decision.many && decision.many.predicted)); |
| 43 | |
| 44 | // 3. State escalates to amber after a synthetic burst. Feeding the real module |
| 45 | // a run of costly turns at "now" should push the live rate well past the |
| 46 | // fallback baseline. |
| 47 | const level = await page.evaluate(() => { |
| 48 | const now = Date.now(); |
| 49 | for (let i = 0; i < 8; i++) window.DaimondGovernor.observe({ t: now - i * 2000, u: 0.60 }); |
| 50 | return window.DaimondGovernor.status().level; |
| 51 | }); |
| 52 | check('a costly burst reads as amber (or tripped)', level === 'amber' || level === 'tripped', 'level ' + level); |
| 53 | |
| 54 | // 4. Setting a budget is honoured by the next decision. |
| 55 | const budgeted = await page.evaluate(() => { |
| 56 | window.DaimondGovernor.setBudget(0.05); // a tiny pace budget |
| 57 | const a = window.DaimondGovernor.assessDispatch(1); |
| 58 | window.DaimondGovernor.setBudget(null); // back to auto |
| 59 | return a.needsConfirm; |
| 60 | }); |
| 61 | check('a tiny user budget makes even one agent ask', budgeted === true); |
| 62 | |
| 63 | // 5. No console errors from the governor. The gateway is deliberately not run |
| 64 | // in this minimal stack, so its proxied /api/ calls answer 502 — that noise |
| 65 | // is expected and filtered; anything else is a real fault. |
| 66 | const real = s.errs.filter(e => !/502|Bad Gateway|Failed to load resource/i.test(e)); |
| 67 | check('no governor-related console errors', real.length === 0, real.slice(0, 3).join(' | ')); |
| 68 | |
| 69 | await shot(s, 'governor'); |
| 70 | await s.close(); |
| 71 | |
| 72 | console.log('\n' + ok.length + ' ok, ' + bad.length + ' failed'); |
| 73 | process.exit(bad.length ? 1 : 0); |