/** * Adversarial integration tests. * node dev/constraints/integration.test.js * * The engine test suite proves the state machine. This one attacks everything * around it: malformed rulebooks, hostile contexts, corrupt artefacts, absurd * numbers, and every path where a module could quietly produce a confident * answer it has not earned. * * The bar throughout: a module may return a correct answer or refuse. It may * never guess, default, impute, or crash. * * Zero dependencies, zero network. */ 'use strict'; const CE = require('./engine'); const { maneuverRulebook, classify, rightOfWay, screeningInterval, LIMITS } = require('./rulebooks/orbital'); const { reentryRulebook } = require('./rulebooks/reentry'); const { releaseRulebook, SCENARIOS } = require('./rulebooks/release-gate'); const CV = require('./voting'); const { PropellantLedger } = require('./ledger'); const RE = require('./reentry'); const CF = require('./conformal'); const A = require('./authoring'); const FL = require('./flares'); const SW = require('./spaceweather'); let pass = 0, fail = 0; const G = '\x1b[32m', R = '\x1b[31m', B = '\x1b[1m', X = '\x1b[0m'; const ok = (n, c, x) => { if (c) { pass++; console.log(` ${G}✓${X} ${n}`); } else { fail++; console.log(` ${R}✗${X} ${n}${x ? ' → ' + x : ''}`); } }; const eq = (n, a, b) => ok(n, a === b, `got ${JSON.stringify(a)}, want ${JSON.stringify(b)}`); const sec = (t) => console.log(`\n${B}${t}${X}`); /** Assert a call does not throw, whatever it is fed. */ const noThrow = (n, fn) => { try { fn(); ok(n, true); } catch (e) { ok(n, false, 'threw: ' + e.message); } }; const NOW = Date.parse('2026-08-22T12:00:00Z'); const H = 3600000; // --------------------------------------------------------------------------- sec('ENGINE — hostile rulebooks and contexts'); noThrow('rule with no evaluate() does not crash the engine', () => { const r = CE.evaluate({ rulebook: { id: 'x', rules: [{ id: 'A', title: 'no evaluator', class: 'HARD' }] }, context: {}, now: NOW }); if (r.rules[0].state !== CE.STATE.UNEVALUATED) throw new Error('became ' + r.rules[0].state); }); noThrow('evaluate() returning garbage becomes UNEVALUATED', () => { const r = CE.evaluate({ rulebook: { id: 'x', rules: [{ id: 'A', class: 'HARD', evaluate: () => 42 }] }, context: {}, now: NOW }); if (r.rules[0].state !== CE.STATE.UNEVALUATED) throw new Error('became ' + r.rules[0].state); }); noThrow('evaluate() returning an unknown state string is tolerated', () => { CE.evaluate({ rulebook: { id: 'x', rules: [{ id: 'A', class: 'HARD', evaluate: () => ({ state: 'BANANA' }) }] }, context: {}, now: NOW }); }); noThrow('null context does not crash', () => CE.evaluate({ rulebook: maneuverRulebook, context: null, now: NOW })); noThrow('undefined context does not crash', () => CE.evaluate({ rulebook: maneuverRulebook, context: undefined, now: NOW })); noThrow('context with circular reference does not crash', () => { const c = { conj: {} }; c.self = c; CE.evaluate({ rulebook: maneuverRulebook, context: c, now: NOW }); }); noThrow('waivers as a non-array is tolerated', () => CE.evaluate({ rulebook: maneuverRulebook, context: {}, waivers: 'nope', now: NOW })); noThrow('empty rulebook is tolerated', () => CE.evaluate({ rulebook: { id: 'e', rules: [] }, context: {}, now: NOW })); { const r = CE.evaluate({ rulebook: { id: 'e', rules: [] }, context: {}, now: NOW }); eq('empty rulebook signals COMPLETE', r.signal, CE.SIGNAL.COMPLETE); eq('...with progress 0 (nothing applicable)', r.progress, 0); } { let threw = false; try { CE.evaluate({ rulebook: { id: 'x' }, context: {} }); } catch { threw = true; } ok('rulebook without a rules array throws rather than passing', threw); } { // A rule that mutates the context must not corrupt other rules' view of it. const ctx = { conj: { min_range_km: 5 } }; CE.evaluate({ rulebook: { id: 'm', rules: [ { id: 'A', class: 'SOFT', evaluate: (c) => { c.conj.min_range_km = 999; return CE.R.pass('ok'); } }, { id: 'B', class: 'SOFT', evaluate: (c) => CE.R.pass('saw ' + c.conj.min_range_km) }, ] }, context: ctx, now: NOW, }); ok('a mutating rule is visible but does not crash later rules', true); } sec('ENGINE — absurd numbers'); for (const v of [NaN, Infinity, -Infinity, null, undefined, '', 'abc', {}, [], -0]) { noThrow(`miss_range = ${JSON.stringify(v)} handled`, () => { CE.evaluate({ rulebook: maneuverRulebook, context: { conj: { min_range_km: v, probability: v, tca_ms: v, tle_age_days: v } }, now: NOW }); }); } { const r = CE.evaluate({ rulebook: maneuverRulebook, context: { conj: { min_range_km: NaN, probability: NaN } }, now: NOW }); ok('NaN inputs never produce SATISFIED on the rules that read them', !['FR-01', 'FR-12'].some((id) => r.rules.find((x) => x.id === id).state === CE.STATE.SATISFIED)); } sec('ENGINE — rollup edge cases'); noThrow('rollup(null)', () => CE.rollup(null)); noThrow('rollup([null, undefined])', () => CE.rollup([null, undefined])); eq('rollup of nothing has 0 items', CE.rollup([]).items, 0); { const one = CE.evaluate({ rulebook: { id: 'x', rules: [{ id: 'A', class: 'HARD', evaluate: () => CE.R.fail('no') }] }, context: {}, now: NOW }); eq('rollup takes the worst state', CE.rollup([one, one]).signal, CE.SIGNAL.BLOCKED); } // --------------------------------------------------------------------------- sec('CLASSIFY / RIGHT OF WAY — hostile names'); for (const n of [null, undefined, '', ' ', 123, {}, [], '', 'A'.repeat(5000)]) { noThrow(`classify(${JSON.stringify(n)?.slice(0, 20)}) does not crash`, () => classify(n)); } eq('empty name is unknown, not a guess', classify(''), null); eq('whitespace name is unknown', classify(' '), null); noThrow('rightOfWay with nulls', () => rightOfWay(null, null, null, null)); { const rw = rightOfWay('A', 'B', 'NONMANEUVERABLE', 'NONMANEUVERABLE'); ok('debris x debris names nobody as the mover', rw.mover === null && rw.resolved === true); const rw2 = rightOfWay('A', 'B', 'MANEUVERABLE', 'MANEUVERABLE'); ok('same class is explicitly UNRESOLVED, not arbitrated', rw2.resolved === false); } // --------------------------------------------------------------------------- sec('VOTING — degenerate clusters'); { const blocked = CE.evaluate({ rulebook: { id: 'x', rules: [{ id: 'FR-03', title: 't', class: 'HARD', waivable: false, evaluate: () => CE.R.fail('no') }] }, context: {}, now: NOW }); noThrow('poll with no nodes', () => CV.poll([], blocked, 3)); noThrow('poll with null nodes', () => CV.poll(null, blocked, 3)); noThrow('poll with unknown node ids', () => CV.poll([{ id: 999, name: 'X', online: true }], blocked, 3)); eq('empty cluster cannot approve', CV.poll([], blocked, 3).status, 'DENIED'); const p = CV.poll([{ id: 1, online: true }], blocked, 3); eq('single node cannot reach quorum 3', p.status, 'DENIED'); ok('every vote carries a reason even for unknown nodes', CV.poll([{ id: 999, name: 'X', online: true }], blocked, 3).votes.every((v) => v.reason && v.reason.length)); eq('offline nodes are excluded from the poll', CV.poll([{ id: 1, online: false }, { id: 2, online: true }], blocked, 3).votes.length, 1); } { // Emergency must refuse a non-negotiable violation and refuse unknowns. const nn = CE.evaluate({ rulebook: { id: 'x', rules: [{ id: 'FR-03', title: 't', class: 'HARD', waivable: false, evaluate: () => CE.R.fail('no') }] }, context: {}, now: NOW }); ok('emergency refuses a non-negotiable violation', CV.emergencyWaivers(nn).allowed === false); const un = CE.evaluate({ rulebook: { id: 'x', rules: [{ id: 'Z', title: 't', class: 'HARD', waivable: true, evaluate: () => CE.R.unknown('no data') }] }, context: {}, now: NOW }); ok('emergency refuses while a rule is unevaluated', CV.emergencyWaivers(un).allowed === false); const cl = CE.evaluate({ rulebook: { id: 'x', rules: [{ id: 'Z', title: 't', class: 'HARD', waivable: true, evaluate: () => CE.R.pass('fine') }] }, context: {}, now: NOW }); ok('emergency on a clean report allows, with no waivers', CV.emergencyWaivers(cl).allowed === true && CV.emergencyWaivers(cl).waivers.length === 0); } // --------------------------------------------------------------------------- sec('LEDGER — arithmetic cannot be cheated'); { const L = new PropellantLedger(); noThrow('get("") is safe', () => L.get('')); eq('get("") is null, not a phantom asset', L.get(''), null); noThrow('debit with NaN', () => L.debit('SAT', NaN)); ok('NaN debit refused', L.debit('SAT', NaN).ok === false); ok('negative debit does not create propellant', (() => { const s = new PropellantLedger(); s.seed('S', { used_ms: 5 }); const before = s.get('S').ca_remaining_ms; s.debit('S', -100); return s.get('S').ca_remaining_ms <= before; })()); const s2 = new PropellantLedger(); s2.seed('S', { used_ms: 14.9 }); eq('a burn beyond the allocation is refused', s2.debit('S', 5).ok, false); ok('...and the balance is untouched after a refusal', s2.get('S').ca_used_ms === 14.9); ok('the disposal reserve is never reachable', (() => { const s = new PropellantLedger(); s.seed('S', { used_ms: 0 }); for (let i = 0; i < 100; i++) s.debit('S', 1); return s.get('S').ca_used_ms <= s.get('S').ca_allocation_ms; })()); ok('seeded history is stable across instances', new PropellantLedger().get('STARLINK-1008').ca_used_ms === new PropellantLedger().get('STARLINK-1008').ca_used_ms); } // --------------------------------------------------------------------------- sec('RE-ENTRY — determinism and refusal'); { const o = { entry_lat: -20, entry_lon: -140, inclination_deg: 53, ballistic_coefficient: 80, kp: 2, seed: 'fixed' }; const a = RE.footprint(o), b = RE.footprint(o); eq('identical inputs give an identical footprint', a.span_km, b.span_km); eq('...and identical population exposure', a.mean_population_density, b.mean_population_density); const c = RE.footprint({ ...o, seed: 'other' }); ok('a different seed gives a different sample path', c.span_km !== a.span_km || c.mean_population_density !== a.mean_population_density); ok('missing Kp refuses rather than assuming quiet', RE.footprint({ ...o, kp: undefined }).unresolved === true); ok('null Kp refuses', RE.footprint({ ...o, kp: null }).unresolved === true); noThrow('NaN Kp', () => RE.footprint({ ...o, kp: NaN })); eq('casualty of an unresolved footprint is null', RE.casualtyExpectancy({ unresolved: true }, 3.5), null); eq('casualty with an unknown casualty area is null', RE.casualtyExpectancy(a, null), null); eq('casualty with NaN area is null', RE.casualtyExpectancy(a, NaN), null); eq('consequence of an unresolved footprint is null', RE.consequenceProfile({ unresolved: true }), null); for (const [lat, lon] of [[91, 0], [-91, 0], [0, 181], [0, -181], [NaN, NaN]]) { noThrow(`cellAt(${lat}, ${lon}) clamps rather than throwing`, () => RE.cellAt(lat, lon)); } ok('an extreme ballistic coefficient still produces a finite span', Number.isFinite(RE.footprint({ ...o, ballistic_coefficient: 100000 }).span_km)); } // --------------------------------------------------------------------------- sec('CONFORMAL — refuses to fabricate a guarantee'); { ok('too few pairs is refused', CF.calibrate([{ predicted: 1, actual: 1 }]).ok === false); ok('empty is refused', CF.calibrate([]).ok === false); noThrow('null input', () => CF.calibrate(null)); const pairs = Array.from({ length: 200 }, (_, i) => ({ predicted: 1 + (i % 7) * 0.1, actual: 1 + (i % 5) * 0.12 })); const cal = CF.calibrate(pairs); ok('200 pairs calibrate', cal.ok === true); ok('coverage is reported', cal.coverage === 0.95); eq('interval on a bad calibrator is null', CF.interval({ ok: false }, 1), null); eq('interval on a non-positive value in log space is null', CF.interval(cal, 0), null); const iv = CF.interval(cal, 1); ok('the interval brackets the point estimate', iv.lower <= iv.point && iv.point <= iv.upper); eq('a straddling interval is UNEVALUATED', CF.decide({ point: 1, lower: 0.5, upper: 2, coverage: 0.95 }, 1, 'below').verdict, 'UNEVALUATED'); eq('an interval entirely below the limit is SATISFIED', CF.decide({ point: 0.1, lower: 0.05, upper: 0.2, coverage: 0.95 }, 1, 'below').verdict, 'SATISFIED'); eq('an interval entirely above is VIOLATED', CF.decide({ point: 9, lower: 8, upper: 10, coverage: 0.95 }, 1, 'below').verdict, 'VIOLATED'); eq('decide(null) is UNEVALUATED', CF.decide(null, 1).verdict, 'UNEVALUATED'); // Wired lookups ok('a calibrated age returns an interval', !!screeningInterval(0.8)); eq('an uncalibrated age returns null, not a neighbour', screeningInterval(30), null); eq('NaN age returns null', screeningInterval(NaN), null); } // --------------------------------------------------------------------------- sec('AUTHORING — the model does not get to decide'); { const base = { id: 'ZZ-1', key: 'K', title: 'A title', field: 'conj.min_range_km', op: 'lte', value: 5, class: 'SOFT', waivable: true, authority: 'operator-declared', rationale: 'x'.repeat(40) }; ok('a well-formed rule validates', A.validate(base, []).ok === true); const bad = { 'field not in the allow-list': { ...base, field: 'process.env.SECRET' }, 'nested traversal field': { ...base, field: '__proto__.polluted' }, 'unsupported operator': { ...base, op: 'exec' }, 'operator as code': { ...base, op: '(()=>{})' }, 'missing authority': { ...base, authority: '' }, 'rationale too short': { ...base, rationale: 'no' }, 'class not HARD/SOFT': { ...base, class: 'MEDIUM' }, 'waivable not boolean': { ...base, waivable: 'yes' }, 'value not numeric': { ...base, value: 'five' }, 'bad id shape': { ...base, id: 'DROP TABLE' }, 'boolean field with numeric op': { ...base, field: 'plan.clear_vs_catalogue', op: 'lte', value: 1 }, }; for (const [name, d] of Object.entries(bad)) ok(`refused: ${name}`, A.validate(d, []).ok === false); ok('a model declining honestly is surfaced as such', A.validate({ error: 'cannot express that' }, []).model_declined === true); noThrow('validate(null)', () => A.validate(null, [])); noThrow('validate on an array', () => A.validate([], [])); // Prototype pollution must be impossible. const store = new A.AuthoredRules(); store.add({ ...base, id: 'ZZ-2', field: 'conj.min_range_km' }, []); const compiled = store.rules[0]; noThrow('compiled rule runs on an empty context', () => compiled.evaluate({ now: NOW })); eq('...and reports UNEVALUATED, not a pass', compiled.evaluate({ now: NOW }).state, CE.STATE.UNEVALUATED); ok('Object.prototype was not polluted', ({}).polluted === undefined); // Probe must expose an inverted rule. const inv = A.probe(A.compile({ ...base, id: 'ZZ-3', field: 'cluster.online_nodes', op: 'lt', value: 3 })); const cor = A.probe(A.compile({ ...base, id: 'ZZ-4', field: 'cluster.online_nodes', op: 'gte', value: 3 })); ok('probing distinguishes an inverted rule from a correct one', JSON.stringify(inv.map((p) => p.result)) !== JSON.stringify(cor.map((p) => p.result))); ok('TLA+ is emitted for an authored rule', A.toTla(compiled).includes('status = "APPROVED"')); ok('a non-negotiable authored rule also emits a never-waived invariant', A.toTla(A.compile({ ...base, id: 'ZZ-5', waivable: false })).includes('NeverWaived')); } // --------------------------------------------------------------------------- sec('FLARES — parser robustness'); { eq('classify(0) is null', FL.classify(0), null); eq('classify(-1) is null', FL.classify(-1), null); eq('classify(NaN) is null', FL.classify(NaN), null); ok('an X-class flux classifies as X', FL.classify(2e-4).startsWith('X')); ok('an M-class flux classifies as M', FL.classify(5e-5).startsWith('M')); eq('R-scale of an X1 flare is R3', FL.rScale(1.2e-4).scale, 'R3'); eq('R-scale below M1 is R0', FL.rScale(1e-6).scale, 'R0'); eq('rScale(NaN) is null', FL.rScale(NaN), null); eq('align([]) is empty', FL.align([]).length, 0); noThrow('align on malformed records', () => FL.align([{ time_tag: 'nonsense', flux: 'x', energy: 'z' }, null, {}])); ok('records missing one band are dropped, not half-used', FL.align([{ time_tag: '2026-01-01T00:00:00Z', flux: 1e-6, energy: '0.1-0.8nm' }]).length === 0); ok('forecast on a short series refuses', FL.forecast([{ t: 1, soft: 1e-6, hard: 1e-8 }]).available === false); const flat = Array.from({ length: 120 }, (_, i) => ({ t: NOW + i * 60000, soft: 1e-6, hard: 1e-8 })); const f = FL.forecast(flat); ok('a flat quiet series forecasts no blackout', f.available && f.blackout_expected === false); eq('...and predicts no impact', FL.predictedImpact(f).expected, false); eq('predictedImpact(null) is null', FL.predictedImpact(null), null); eq('nowcast on a flat series finds no flare', FL.nowcast(flat).length, 0); noThrow('nowcast on an empty series', () => FL.nowcast([])); } // --------------------------------------------------------------------------- sec('SPACE WEATHER — derived zones'); { eq('gScale below storm', SW.gScale(2), 'G0'); eq('gScale Kp 5 is G1', SW.gScale(5), 'G1'); eq('gScale Kp 9 is G5', SW.gScale(9), 'G5'); eq('gScale is clamped above 9', SW.gScale(99), 'G5'); eq('gScale(NaN) is G0', SW.gScale(NaN), 'G0'); eq('zones(null) is null', SW.zones(null), null); eq('groundSegment(null) is null', SW.groundSegment(null), null); const quiet = SW.groundSegment({ kp: 1, scale_s: 'S0' }); eq('all stations reachable when quiet', quiet.stations_available, quiet.stations_assigned); const storm = SW.groundSegment({ kp: 9, scale_s: 'S3' }); ok('a radiation storm takes polar stations offline', storm.stations_available < storm.stations_assigned); ok('...and never takes them all, at these latitudes', storm.stations_available > 0); ok('every offline station states a reason', storm.stations.filter((s) => !s.available).every((s) => s.reason)); eq('an unknown replay is null', SW.replay('nope'), null); ok('the gannon replay is complete', !!SW.replay('gannon').groundSegment); } // --------------------------------------------------------------------------- sec('CROSS-RULEBOOK — invariants that must hold everywhere'); { const books = [maneuverRulebook, reentryRulebook, releaseRulebook]; const all = books.flatMap((b) => b.rules); ok('every rule has a unique id within its book', books.every((b) => new Set(b.rules.map((r) => r.id)).size === b.rules.length)); ok('every rule cites an authority', all.every((r) => r.authority && r.authority.length > 4)); ok('every rule explains itself', all.every((r) => r.rationale && r.rationale.length > 20)); ok('every rule states its requirement', all.every((r) => r.requirement && r.requirement.length > 10)); ok('no SOFT rule is marked non-negotiable', all.every((r) => !(r.class === 'SOFT' && r.waivable === false))); ok('every rule has an evaluate()', all.every((r) => typeof r.evaluate === 'function')); // Every rule, fed an empty context, must report UNEVALUATED or NOT_APPLICABLE. // Never SATISFIED — that would be a pass earned from no evidence at all. for (const b of books) { const rep = CE.evaluate({ rulebook: b, context: {}, now: NOW }); const wrong = rep.rules.filter((r) => r.state === CE.STATE.SATISFIED); ok(`${b.id}: no rule is SATISFIED on an empty context`, wrong.length === 0, wrong.map((r) => r.id).join(',')); ok(`${b.id}: an empty context is never authorised`, rep.authorised === false); } // A waiver on a non-negotiable rule must never take effect, in any book. for (const b of books) { const nn = b.rules.filter((r) => r.waivable === false); if (!nn.length) continue; const waivers = nn.map((r) => ({ rule_id: r.id, party: 'attacker', reason: 'because' })); const rep = CE.evaluate({ rulebook: b, context: {}, waivers, now: NOW }); ok(`${b.id}: non-negotiable rules cannot be waived`, rep.rules.filter((r) => r.waivable === false).every((r) => r.state !== CE.STATE.WAIVED)); } } sec('RELEASE GATE — the portability proof still holds'); { const seen = new Set(); for (const [id, s] of Object.entries(SCENARIOS)) { const r = CE.evaluate({ rulebook: releaseRulebook, context: s.context, now: NOW }); seen.add(r.signal); } ok('the non-space rulebook reaches every one of the four states', seen.size === 4, [...seen].join(',')); const w = [{ rule_id: 'RG-02', party: 'release manager', reason: 'ship it' }]; eq('waiving a critical CVE does not unblock a release', CE.evaluate({ rulebook: releaseRulebook, context: SCENARIOS.critical_cve.context, waivers: w, now: NOW }).signal, CE.SIGNAL.BLOCKED); } console.log(`\n${fail === 0 ? G : R}${B}${pass} passed, ${fail} failed${X}\n`); process.exit(fail === 0 ? 0 : 1);