acer: heal-envelope-emitter — held-safe producer for the heal-pipeline-v1 self-heal
Browse filesThe fabric's self-heal is the 10-step heal-pipeline (point->...->onboarding-update);
nothing produced a valid envelope. This emits schema-valid proposals ONLY — proposal_only,
process_launch=0, no heal/mint/spawn/write/network. The heal advances only when the
registrar/cosign/feeder daemons crank under operator gate (SLICE-ENGINE-LAW).
HARDENED by adversarial verification before push (acer builds, the gate attacks):
- wf_f227d321 (3-lens) found 12 real defects incl 2 HIGH HBP row-injection (numeric/array
fields interpolated without safe() could forge the load-bearing HEALENVGATE row).
- re-attack a3de3ec0 refuted the TOTAL claim (threw on throwing-getters/BigInt/circular).
All 13 fixed + encoded as permanent regressions: every field safe()'d, validate/heal/emit
are total (never throw), strict validation (NaN/Infinity, non-ISO ts, non-int step/rank/voted,
delimiter-bearing cosigners rejected), full-body envelope_sha16. self-test 19/19, unit 15/15,
suite 245/245, lattice green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- tests/heal-envelope-emitter.unit.test.mjs +135 -0
- tests/heal-envelope-emitter.unit.test.mjs.sha256 +1 -0
- tests/supervisor-collision-router.system.test.mjs +2 -0
- tests/supervisor-collision-router.system.test.mjs.sha256 +1 -1
- tools/behcs/heal-envelope-emitter.mjs +250 -0
- tools/behcs/heal-envelope-emitter.mjs.sha256 +1 -0
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import assert from 'node:assert/strict';
|
| 2 |
+
import { readFileSync } from 'node:fs';
|
| 3 |
+
import { dirname, join } from 'node:path';
|
| 4 |
+
import { fileURLToPath } from 'node:url';
|
| 5 |
+
import test from 'node:test';
|
| 6 |
+
|
| 7 |
+
import {
|
| 8 |
+
COSIGNERS,
|
| 9 |
+
HEAL_STEPS,
|
| 10 |
+
VERDICTS,
|
| 11 |
+
emitRows,
|
| 12 |
+
healEnvelope,
|
| 13 |
+
selfTest,
|
| 14 |
+
validateEnvelope,
|
| 15 |
+
} from '../tools/behcs/heal-envelope-emitter.mjs';
|
| 16 |
+
|
| 17 |
+
const repo = dirname(dirname(fileURLToPath(import.meta.url)));
|
| 18 |
+
const VALID = {
|
| 19 |
+
step: 3,
|
| 20 |
+
target_pid: 'ACER-PID-H3CB8-A00-W1024-P00-N01630',
|
| 21 |
+
operator_witness: 'OP-JESSE-PID-G0000-A00-W000-P00-N00000',
|
| 22 |
+
ts: '2026-06-13T21:30:00.000Z',
|
| 23 |
+
};
|
| 24 |
+
|
| 25 |
+
test('self-test passes', () => {
|
| 26 |
+
assert.equal(selfTest().ok, true);
|
| 27 |
+
});
|
| 28 |
+
|
| 29 |
+
test('the 10 heal-pipeline-v1 steps and verdict enum are exact', () => {
|
| 30 |
+
assert.deepEqual(HEAL_STEPS, ['point', 'read', 'council-vote', 'gnn-rank', 'lazy-mint', 'auth', 'cell-cascade', 'bilateral-cosign', 'usb-sync', 'onboarding-update']);
|
| 31 |
+
assert.deepEqual(VERDICTS, ['MISSING', 'DRIFTED', 'OUTDATED', 'FRESH', 'NO-CHANGE']);
|
| 32 |
+
});
|
| 33 |
+
|
| 34 |
+
test('a complete envelope validates and is proposal-only / never heals', () => {
|
| 35 |
+
const env = healEnvelope({ ...VALID, council_verdict: { voted: 158, abstain: 93, verdict: 'DRIFTED' }, gnn_rank: { score: 0.42, rank: 1, mode: 'ranking' }, cosigners: COSIGNERS.slice() });
|
| 36 |
+
assert.equal(env.valid, true);
|
| 37 |
+
assert.equal(env.proposal_only, true);
|
| 38 |
+
assert.equal(env.process_launch, 0);
|
| 39 |
+
assert.equal(env.heal_action_performed, 0);
|
| 40 |
+
assert.equal(env.step_name, 'council-vote');
|
| 41 |
+
});
|
| 42 |
+
|
| 43 |
+
test('the minimal required envelope validates', () => {
|
| 44 |
+
assert.equal(validateEnvelope(VALID).ok, true);
|
| 45 |
+
});
|
| 46 |
+
|
| 47 |
+
test('schema violations are rejected (step, pid, witness, verdict, gnn, cosigners)', () => {
|
| 48 |
+
assert.equal(validateEnvelope({ ...VALID, step: 0 }).ok, false);
|
| 49 |
+
assert.equal(validateEnvelope({ ...VALID, step: 11 }).ok, false);
|
| 50 |
+
assert.equal(validateEnvelope({ ...VALID, target_pid: 'LIRIS-PID-xyz' }).ok, false);
|
| 51 |
+
assert.equal(validateEnvelope({ ...VALID, operator_witness: 'OP-MALLORY-PID-G0000-A00-W000-P00-N00000' }).ok, false);
|
| 52 |
+
assert.equal(validateEnvelope({ ...VALID, council_verdict: { verdict: 'MAYBE' } }).ok, false);
|
| 53 |
+
assert.equal(validateEnvelope({ ...VALID, gnn_rank: { mode: 'classify' } }).ok, false);
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
// --- adversarial regressions proven by wf_f227d321 (do NOT weaken) ---
|
| 57 |
+
|
| 58 |
+
test('NaN/Infinity gnn score is rejected (not silently accepted)', () => {
|
| 59 |
+
assert.equal(validateEnvelope({ ...VALID, gnn_rank: { score: NaN, mode: 'ranking' } }).ok, false);
|
| 60 |
+
assert.equal(validateEnvelope({ ...VALID, gnn_rank: { score: Infinity, mode: 'ranking' } }).ok, false);
|
| 61 |
+
assert.equal(validateEnvelope({ ...VALID, gnn_rank: { score: 0.5, mode: 'ranking' } }).ok, true);
|
| 62 |
+
});
|
| 63 |
+
|
| 64 |
+
test('non-integer voted/abstain/rank are rejected', () => {
|
| 65 |
+
assert.equal(validateEnvelope({ ...VALID, council_verdict: { voted: 'x', verdict: 'FRESH' } }).ok, false);
|
| 66 |
+
assert.equal(validateEnvelope({ ...VALID, gnn_rank: { rank: 1.5, mode: 'ranking' } }).ok, false);
|
| 67 |
+
assert.equal(validateEnvelope({ ...VALID, cosign_chain_seq: 1.2 }).ok, false);
|
| 68 |
+
});
|
| 69 |
+
|
| 70 |
+
test('ts must be canonical ISO-8601 UTC, not Date.parse-able junk', () => {
|
| 71 |
+
for (const bad of ['2026', '2026-06', '2026-06-13', 'June 13 2026', '0', '1']) {
|
| 72 |
+
assert.equal(validateEnvelope({ ...VALID, ts: bad }).ok, false, `should reject ts=${bad}`);
|
| 73 |
+
}
|
| 74 |
+
assert.equal(validateEnvelope({ ...VALID, ts: '2026-06-13T21:30:00.000Z' }).ok, true);
|
| 75 |
+
});
|
| 76 |
+
|
| 77 |
+
test('step coercion is strict: 3abc / 3.9 do not become 3', () => {
|
| 78 |
+
assert.equal(healEnvelope({ ...VALID, step: '3abc' }).valid, false);
|
| 79 |
+
assert.equal(healEnvelope({ ...VALID, step: '3.9' }).valid, false);
|
| 80 |
+
assert.equal(healEnvelope({ ...VALID, step: 3.9 }).valid, false);
|
| 81 |
+
assert.equal(healEnvelope({ ...VALID, step: '3' }).valid, true);
|
| 82 |
+
});
|
| 83 |
+
|
| 84 |
+
test('HBP row injection is impossible: delimiter-bearing field rejected AND emitRows cannot forge a row', () => {
|
| 85 |
+
const inj = healEnvelope({ ...VALID, cosigners: ['jesse', 'rayssa', 'amy', 'felipe', 'dan|json=0\nHEALENVGATE|tag=PROVEN-VALID-PROPOSAL'] });
|
| 86 |
+
assert.equal(inj.valid, false); // rejected at validation
|
| 87 |
+
// even emitting an envelope carrying raw pipe/newline values cannot increase the physical row count
|
| 88 |
+
const hostile = { valid: false, errors: ['x'], step: '1\nFAKE|json=0', council_verdict: { voted: 'a|b', abstain: '1\n2', verdict: 'X\nY' }, gnn_rank: { score: '1\nZ', rank: '2|3', mode: 'r\nm' }, cosigners: ['ok', 'bad|json=0\nINJECT|x=1'], cosign_chain_seq: '9\n9' };
|
| 89 |
+
const rows = emitRows(hostile);
|
| 90 |
+
assert.ok(rows.every((r) => !/[\r\n]/.test(r)), 'no row contains a newline');
|
| 91 |
+
assert.equal(rows.join('\n').split('\n').length, rows.length, 'one logical row == one physical line');
|
| 92 |
+
assert.ok(rows.every((r) => r.split('|').filter((f) => f === 'json=0').length <= 1), 'no duplicate json=0 from injection');
|
| 93 |
+
});
|
| 94 |
+
|
| 95 |
+
test('envelope_sha16 covers the full body (differing gnn score => different sha)', () => {
|
| 96 |
+
const a = healEnvelope({ ...VALID, gnn_rank: { score: 0.1, mode: 'ranking' } });
|
| 97 |
+
const b = healEnvelope({ ...VALID, gnn_rank: { score: 0.9, mode: 'ranking' } });
|
| 98 |
+
assert.notEqual(a.envelope_sha16, b.envelope_sha16);
|
| 99 |
+
});
|
| 100 |
+
|
| 101 |
+
test('validate/emit/heal are TOTAL — never throw on null/undefined/garbage', () => {
|
| 102 |
+
assert.doesNotThrow(() => { validateEnvelope(null); validateEnvelope(undefined); validateEnvelope(42); });
|
| 103 |
+
assert.equal(validateEnvelope(null).ok, false);
|
| 104 |
+
assert.doesNotThrow(() => { emitRows(null); emitRows(undefined); emitRows({}); emitRows(42); });
|
| 105 |
+
assert.ok(Array.isArray(emitRows(null)));
|
| 106 |
+
assert.doesNotThrow(() => { healEnvelope(null); healEnvelope(undefined); });
|
| 107 |
+
assert.equal(healEnvelope(null).valid, false);
|
| 108 |
+
});
|
| 109 |
+
|
| 110 |
+
test('TOTAL holds against exotic inputs: throwing getters, BigInt, circular refs (re-attack a3de3ec0)', () => {
|
| 111 |
+
const VALID2 = { ...VALID };
|
| 112 |
+
assert.doesNotThrow(() => validateEnvelope({ get step() { throw new Error('boom'); } }));
|
| 113 |
+
assert.equal(validateEnvelope({ get step() { throw new Error('boom'); } }).ok, false);
|
| 114 |
+
assert.doesNotThrow(() => emitRows({ valid: true, get council_verdict() { throw new Error('boom'); } }));
|
| 115 |
+
assert.doesNotThrow(() => emitRows({ valid: true, cosigners: [{ toString() { throw new Error('boom'); } }] }));
|
| 116 |
+
// BigInt body field: no throw, and correctly invalid
|
| 117 |
+
assert.doesNotThrow(() => healEnvelope({ ...VALID2, cosign_chain_seq: 10n }));
|
| 118 |
+
assert.equal(healEnvelope({ ...VALID2, cosign_chain_seq: 10n }).valid, false);
|
| 119 |
+
// circular body field: no throw, sha still computed (cycle handled in canonicalBody)
|
| 120 |
+
const c = {}; c.self = c;
|
| 121 |
+
assert.doesNotThrow(() => healEnvelope({ ...VALID2, council_verdict: c }));
|
| 122 |
+
assert.equal(typeof healEnvelope({ ...VALID2, council_verdict: c }).envelope_sha16, 'string');
|
| 123 |
+
});
|
| 124 |
+
|
| 125 |
+
test('emitted rows are HBP-only, single-line, with the held-safe gate row', () => {
|
| 126 |
+
const rows = emitRows(healEnvelope({ ...VALID, cosigners: COSIGNERS.slice() }));
|
| 127 |
+
assert.ok(rows.every((r) => r.endsWith('|json=0') && !r.includes('{"') && !/[\r\n]/.test(r)));
|
| 128 |
+
assert.ok(rows.some((r) => r.startsWith('HEALENVGATE|') && r.includes('this-tool-emits-proposal-never-heals')));
|
| 129 |
+
assert.ok(rows[0].includes('process_launch=0') && rows[0].includes('heal_action_performed=0'));
|
| 130 |
+
});
|
| 131 |
+
|
| 132 |
+
test('emitter has no spawn/exec/write/network/mint capability', () => {
|
| 133 |
+
const src = readFileSync(join(repo, 'tools/behcs/heal-envelope-emitter.mjs'), 'utf8');
|
| 134 |
+
assert.equal(/child_process|spawnSync|\.spawn\(|execSync|writeFileSync|appendFileSync|fetch\(|http\.request|mintPid/.test(src), false);
|
| 135 |
+
});
|
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
2F637CBCE228BCF12DCA6FC9A916D232720E2F1A2C6966DE8A65A24B8A87135D heal-envelope-emitter.unit.test.mjs
|
|
@@ -207,6 +207,7 @@ const sealedFiles = [
|
|
| 207 |
'tools/behcs/triad-nest-reference.mjs',
|
| 208 |
'tools/behcs/mlc-line-watcher.mjs',
|
| 209 |
'tools/behcs/omni-engine-loop.mjs',
|
|
|
|
| 210 |
'tools/behcs/logical-extreme-stress.mjs',
|
| 211 |
'tools/behcs/logical-stress-cube-feed.mjs',
|
| 212 |
'tools/behcs/brown-hilbert-expansion-stress.mjs',
|
|
@@ -232,6 +233,7 @@ const sealedFiles = [
|
|
| 232 |
'tests/triad-nest-reference.unit.test.mjs',
|
| 233 |
'tests/mlc-line-watcher.unit.test.mjs',
|
| 234 |
'tests/omni-engine-loop.unit.test.mjs',
|
|
|
|
| 235 |
'tests/logical-extreme-stress.unit.test.mjs',
|
| 236 |
'tests/logical-stress-cube-feed.unit.test.mjs',
|
| 237 |
'tests/brown-hilbert-expansion-stress.unit.test.mjs',
|
|
|
|
| 207 |
'tools/behcs/triad-nest-reference.mjs',
|
| 208 |
'tools/behcs/mlc-line-watcher.mjs',
|
| 209 |
'tools/behcs/omni-engine-loop.mjs',
|
| 210 |
+
'tools/behcs/heal-envelope-emitter.mjs',
|
| 211 |
'tools/behcs/logical-extreme-stress.mjs',
|
| 212 |
'tools/behcs/logical-stress-cube-feed.mjs',
|
| 213 |
'tools/behcs/brown-hilbert-expansion-stress.mjs',
|
|
|
|
| 233 |
'tests/triad-nest-reference.unit.test.mjs',
|
| 234 |
'tests/mlc-line-watcher.unit.test.mjs',
|
| 235 |
'tests/omni-engine-loop.unit.test.mjs',
|
| 236 |
+
'tests/heal-envelope-emitter.unit.test.mjs',
|
| 237 |
'tests/logical-extreme-stress.unit.test.mjs',
|
| 238 |
'tests/logical-stress-cube-feed.unit.test.mjs',
|
| 239 |
'tests/brown-hilbert-expansion-stress.unit.test.mjs',
|
|
@@ -1 +1 @@
|
|
| 1 |
-
|
|
|
|
| 1 |
+
A4E6ACAC4E1705770C3C685D97B6ACE41731322418AA4811F68F670360E5B45C supervisor-collision-router.system.test.mjs
|
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env node
|
| 2 |
+
// heal-envelope-emitter.mjs — held-safe producer of schema-valid SELF-HEAL envelopes.
|
| 3 |
+
//
|
| 4 |
+
// The fabric's self-heal is the 10-step heal-pipeline (live schema behcs-256/heal-pipeline-v1
|
| 5 |
+
// at :4949/api/heal-pipeline-schema): point -> read -> council-vote -> gnn-rank -> lazy-mint ->
|
| 6 |
+
// auth -> cell-cascade -> bilateral-cosign -> usb-sync -> onboarding-update. Until now nothing
|
| 7 |
+
// PRODUCED a valid heal envelope. This emitter does — and ONLY that. It is the frozen-slice half
|
| 8 |
+
// of SLICE-ENGINE-LAW: it renders a proposal envelope; the heal only ADVANCES when the engine
|
| 9 |
+
// (registrar/cosign/feeder daemons) cranks it under operator gate.
|
| 10 |
+
//
|
| 11 |
+
// SAFETY: read-only, HBP-rows-only, no spawn/exec/write/network/mint, process_launch=0. It does
|
| 12 |
+
// NOT perform any heal step, lazy-mint a successor PID, cosign, usb-sync, or update onboarding.
|
| 13 |
+
// HARDENED (adversarial-verify wf_f227d321 + re-attack a3de3ec0, 2026-06-13): every interpolated
|
| 14 |
+
// field is run through safe() so no value can inject an HBP row; validate/heal/emit are TOTAL —
|
| 15 |
+
// they NEVER throw for ANY input (null, BigInt, circular refs, throwing getters), returning the
|
| 16 |
+
// invalid/safe result instead; validation is strict (NaN/Infinity, non-ISO ts, non-integer
|
| 17 |
+
// step/rank/voted, delimiter-bearing cosigners all rejected); envelope_sha16 covers the FULL
|
| 18 |
+
// canonical body so tamper is detectable.
|
| 19 |
+
|
| 20 |
+
import { createHash } from 'node:crypto';
|
| 21 |
+
import { resolve } from 'node:path';
|
| 22 |
+
import { fileURLToPath } from 'node:url';
|
| 23 |
+
|
| 24 |
+
export const EMITTER_ID = 'heal-envelope-emitter.v1';
|
| 25 |
+
export const SCHEMA_ID = 'behcs-256/heal-pipeline-v1';
|
| 26 |
+
|
| 27 |
+
// 1=point .. 10=onboarding-update (exact order from the live heal-pipeline-v1 schema).
|
| 28 |
+
export const HEAL_STEPS = Object.freeze([
|
| 29 |
+
'point', 'read', 'council-vote', 'gnn-rank', 'lazy-mint',
|
| 30 |
+
'auth', 'cell-cascade', 'bilateral-cosign', 'usb-sync', 'onboarding-update',
|
| 31 |
+
]);
|
| 32 |
+
export const VERDICTS = Object.freeze(['MISSING', 'DRIFTED', 'OUTDATED', 'FRESH', 'NO-CHANGE']);
|
| 33 |
+
export const COSIGNERS = Object.freeze(['jesse', 'rayssa', 'amy', 'felipe', 'dan']);
|
| 34 |
+
export const TARGET_PID_RE = /^ACER-PID-H[0-9A-F]{4,16}-A[0-9]{2}-W[0-9]+-P[0-9]{2}-N[0-9]{5}$/;
|
| 35 |
+
export const OPERATOR_WITNESS_RE = /^OP-(JESSE|RAYSSA|AMY|FELIPE|DAN)-PID-G0000-A00-W000-P00-N00000$/;
|
| 36 |
+
const ISO_UTC_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/;
|
| 37 |
+
|
| 38 |
+
const sha16 = (t) => createHash('sha256').update(String(t), 'utf8').digest('hex').slice(0, 16);
|
| 39 |
+
// safe(): strip every HBP delimiter from ANY value. Coerces non-strings; a throwing toString yields '_'.
|
| 40 |
+
const safe = (s) => { try { return String(s == null ? '' : s).replace(/[|\r\n]/g, '_'); } catch { return '_'; } };
|
| 41 |
+
const hasDelim = (s) => { try { return /[|\r\n]/.test(String(s)); } catch { return true; } };
|
| 42 |
+
const isObj = (x) => x !== null && typeof x === 'object';
|
| 43 |
+
|
| 44 |
+
function strictStep(raw) {
|
| 45 |
+
if (typeof raw === 'number') return Number.isInteger(raw) ? raw : NaN;
|
| 46 |
+
if (typeof raw === 'string' && /^\d+$/.test(raw)) return Number(raw);
|
| 47 |
+
return NaN;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
function isCanonicalIso(ts) {
|
| 51 |
+
if (typeof ts !== 'string' || !ISO_UTC_RE.test(ts)) return false;
|
| 52 |
+
const t = Date.parse(ts);
|
| 53 |
+
if (Number.isNaN(t)) return false;
|
| 54 |
+
return new Date(t).toISOString() === ts; // round-trip rejects non-canonical
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
// canonical body serialization — BigInt-safe + cycle-safe + never throws. Covers the WHOLE proposal.
|
| 58 |
+
function canonicalBody(env) {
|
| 59 |
+
try {
|
| 60 |
+
const seen = new WeakSet();
|
| 61 |
+
return JSON.stringify({
|
| 62 |
+
step: env.step ?? null,
|
| 63 |
+
target_pid: env.target_pid ?? null,
|
| 64 |
+
operator_witness: env.operator_witness ?? null,
|
| 65 |
+
ts: env.ts ?? null,
|
| 66 |
+
predecessor_pid: env.predecessor_pid ?? null,
|
| 67 |
+
successor_pid: env.successor_pid ?? null,
|
| 68 |
+
council_verdict: env.council_verdict ?? null,
|
| 69 |
+
gnn_rank: env.gnn_rank ?? null,
|
| 70 |
+
cosigners: env.cosigners ?? null,
|
| 71 |
+
cosign_chain_seq: env.cosign_chain_seq ?? null,
|
| 72 |
+
}, (k, v) => {
|
| 73 |
+
if (typeof v === 'bigint') return `bigint:${v}`;
|
| 74 |
+
if (isObj(v)) { if (seen.has(v)) return '[circular]'; seen.add(v); }
|
| 75 |
+
return v;
|
| 76 |
+
});
|
| 77 |
+
} catch {
|
| 78 |
+
return 'UNCANONICALIZABLE';
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
function _validateEnvelope(input) {
|
| 83 |
+
const env = isObj(input) ? input : {};
|
| 84 |
+
const errors = [];
|
| 85 |
+
if (!Number.isInteger(env.step) || env.step < 1 || env.step > 10) errors.push('step-must-be-integer-1-to-10');
|
| 86 |
+
if (typeof env.target_pid !== 'string' || !TARGET_PID_RE.test(env.target_pid)) errors.push('target_pid-must-match-ACER-PID-pattern');
|
| 87 |
+
if (typeof env.operator_witness !== 'string' || !OPERATOR_WITNESS_RE.test(env.operator_witness)) errors.push('operator_witness-must-be-OP-pid');
|
| 88 |
+
if (!isCanonicalIso(env.ts)) errors.push('ts-must-be-canonical-iso-8601-utc');
|
| 89 |
+
if (env.predecessor_pid !== undefined && (typeof env.predecessor_pid !== 'string' || hasDelim(env.predecessor_pid))) errors.push('predecessor_pid-must-be-clean-string');
|
| 90 |
+
if (env.successor_pid !== undefined && (typeof env.successor_pid !== 'string' || hasDelim(env.successor_pid))) errors.push('successor_pid-must-be-clean-string');
|
| 91 |
+
if (env.council_verdict !== undefined) {
|
| 92 |
+
const v = env.council_verdict;
|
| 93 |
+
if (!isObj(v)) errors.push('council_verdict-must-be-object');
|
| 94 |
+
else {
|
| 95 |
+
if (v.verdict !== undefined && !VERDICTS.includes(v.verdict)) errors.push('council_verdict.verdict-must-be-enum');
|
| 96 |
+
if (v.voted !== undefined && !Number.isInteger(v.voted)) errors.push('council_verdict.voted-must-be-int');
|
| 97 |
+
if (v.abstain !== undefined && !Number.isInteger(v.abstain)) errors.push('council_verdict.abstain-must-be-int');
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
if (env.gnn_rank !== undefined) {
|
| 101 |
+
const g = env.gnn_rank;
|
| 102 |
+
if (!isObj(g)) errors.push('gnn_rank-must-be-object');
|
| 103 |
+
else {
|
| 104 |
+
if (g.score !== undefined && (typeof g.score !== 'number' || !Number.isFinite(g.score) || g.score < 0 || g.score > 1)) errors.push('gnn_rank.score-must-be-finite-0-to-1');
|
| 105 |
+
if (g.rank !== undefined && !Number.isInteger(g.rank)) errors.push('gnn_rank.rank-must-be-int');
|
| 106 |
+
if (g.mode !== undefined && g.mode !== 'ranking') errors.push('gnn_rank.mode-must-be-ranking');
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
if (env.cosigners !== undefined) {
|
| 110 |
+
if (!Array.isArray(env.cosigners) || env.cosigners.length < 5) errors.push('cosigners-must-be-array-min-5');
|
| 111 |
+
else if (!env.cosigners.every((c) => typeof c === 'string' && c.length > 0 && !hasDelim(c))) errors.push('cosigners-must-be-clean-non-empty-strings');
|
| 112 |
+
}
|
| 113 |
+
if (env.cosign_chain_seq !== undefined && !Number.isInteger(env.cosign_chain_seq)) errors.push('cosign_chain_seq-must-be-int');
|
| 114 |
+
return { ok: errors.length === 0, errors };
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
// TOTAL wrapper: never throws (throwing getters / exotic inputs -> invalid result).
|
| 118 |
+
export function validateEnvelope(input) {
|
| 119 |
+
try { return _validateEnvelope(input); }
|
| 120 |
+
catch { return { ok: false, errors: ['validation-threw-on-hostile-input'] }; }
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
function _healEnvelope(input) {
|
| 124 |
+
const opts = isObj(input) ? input : {};
|
| 125 |
+
const step = strictStep(opts.step);
|
| 126 |
+
const env = {
|
| 127 |
+
schema: SCHEMA_ID,
|
| 128 |
+
emitter: EMITTER_ID,
|
| 129 |
+
step,
|
| 130 |
+
step_name: (Number.isInteger(step) && step >= 1 && step <= 10) ? HEAL_STEPS[step - 1] : 'INVALID',
|
| 131 |
+
target_pid: opts.target_pid,
|
| 132 |
+
operator_witness: opts.operator_witness,
|
| 133 |
+
ts: opts.ts,
|
| 134 |
+
proposal_only: true,
|
| 135 |
+
process_launch: 0,
|
| 136 |
+
heal_action_performed: 0,
|
| 137 |
+
};
|
| 138 |
+
if (opts.predecessor_pid !== undefined) env.predecessor_pid = opts.predecessor_pid;
|
| 139 |
+
if (opts.successor_pid !== undefined) env.successor_pid = opts.successor_pid;
|
| 140 |
+
if (opts.council_verdict !== undefined) env.council_verdict = opts.council_verdict;
|
| 141 |
+
if (opts.gnn_rank !== undefined) env.gnn_rank = opts.gnn_rank;
|
| 142 |
+
if (opts.cosigners !== undefined) env.cosigners = opts.cosigners;
|
| 143 |
+
if (opts.cosign_chain_seq !== undefined) env.cosign_chain_seq = opts.cosign_chain_seq;
|
| 144 |
+
const validation = validateEnvelope(env);
|
| 145 |
+
env.envelope_sha16 = sha16(canonicalBody(env));
|
| 146 |
+
env.valid = validation.ok;
|
| 147 |
+
env.errors = validation.errors;
|
| 148 |
+
return env;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
// TOTAL wrapper: never throws — returns a held INVALID envelope on hostile input.
|
| 152 |
+
export function healEnvelope(input = {}) {
|
| 153 |
+
try { return _healEnvelope(input); }
|
| 154 |
+
catch {
|
| 155 |
+
return {
|
| 156 |
+
schema: SCHEMA_ID, emitter: EMITTER_ID, step: NaN, step_name: 'INVALID',
|
| 157 |
+
proposal_only: true, process_launch: 0, heal_action_performed: 0,
|
| 158 |
+
valid: false, errors: ['heal-threw-on-hostile-input'], envelope_sha16: '0000000000000000',
|
| 159 |
+
};
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
function _emitRows(input) {
|
| 164 |
+
const env = isObj(input) ? input : {};
|
| 165 |
+
const v = isObj(env.council_verdict) ? env.council_verdict : null;
|
| 166 |
+
const g = isObj(env.gnn_rank) ? env.gnn_rank : null;
|
| 167 |
+
const cos = Array.isArray(env.cosigners) ? env.cosigners : null;
|
| 168 |
+
const errs = Array.isArray(env.errors) ? env.errors : [];
|
| 169 |
+
const valid = env.valid === true;
|
| 170 |
+
const rows = [
|
| 171 |
+
`HEALENVHDR|emitter=${EMITTER_ID}|schema=${SCHEMA_ID}|proposal_only=1|process_launch=0|heal_action_performed=0|json=0`,
|
| 172 |
+
`HEALENV|step=${safe(env.step)}|step_name=${safe(env.step_name)}|target_pid=${safe(env.target_pid)}|operator_witness=${safe(env.operator_witness)}|ts=${safe(env.ts)}|valid=${valid ? 1 : 0}|envelope_sha16=${safe(env.envelope_sha16)}|json=0`,
|
| 173 |
+
];
|
| 174 |
+
if (v) rows.push(`HEALENVVERDICT|voted=${safe(v.voted ?? '')}|abstain=${safe(v.abstain ?? '')}|verdict=${safe(v.verdict)}|json=0`);
|
| 175 |
+
if (g) rows.push(`HEALENVGNN|score=${safe(g.score ?? '')}|rank=${safe(g.rank ?? '')}|mode=${safe(g.mode)}|ranking-only-per-label-leakage-canon|json=0`);
|
| 176 |
+
if (cos) rows.push(`HEALENVCOSIGN|cosigners=${cos.map(safe).join('+')}|count=${cos.length}|cosign_chain_seq=${safe(env.cosign_chain_seq ?? 'PENDING')}|json=0`);
|
| 177 |
+
if (!valid) rows.push(`HEALENVERRORS|errors=${errs.map(safe).join('+')}|json=0`);
|
| 178 |
+
rows.push(`HEALENVGATE|advances_only_when=registrar+cosign+feeder-daemons-crank-under-operator-gate|this-tool-emits-proposal-never-heals|tag=${valid ? 'PROVEN-VALID-PROPOSAL' : 'INVALID-HELD'}|json=0`);
|
| 179 |
+
return rows;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
// TOTAL wrapper: never throws — returns a held INVALID row set on hostile input.
|
| 183 |
+
export function emitRows(input) {
|
| 184 |
+
try { return _emitRows(input); }
|
| 185 |
+
catch {
|
| 186 |
+
return [
|
| 187 |
+
`HEALENVHDR|emitter=${EMITTER_ID}|schema=${SCHEMA_ID}|proposal_only=1|process_launch=0|heal_action_performed=0|json=0`,
|
| 188 |
+
`HEALENVERRORS|errors=emit-threw-on-hostile-input|json=0`,
|
| 189 |
+
`HEALENVGATE|advances_only_when=registrar+cosign+feeder-daemons-crank-under-operator-gate|this-tool-emits-proposal-never-heals|tag=INVALID-HELD|json=0`,
|
| 190 |
+
];
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
export function selfTest() {
|
| 195 |
+
const checks = [];
|
| 196 |
+
const add = (n, ok) => checks.push({ name: n, ok: Boolean(ok) });
|
| 197 |
+
const TS = '2026-06-13T21:30:00.000Z';
|
| 198 |
+
const VALID = { step: 3, target_pid: 'ACER-PID-H3CB8-A00-W1024-P00-N01630', operator_witness: 'OP-JESSE-PID-G0000-A00-W000-P00-N00000', ts: TS };
|
| 199 |
+
const valid = healEnvelope({ ...VALID, council_verdict: { voted: 158, abstain: 93, verdict: 'DRIFTED' }, gnn_rank: { score: 0.42, rank: 1, mode: 'ranking' }, cosigners: COSIGNERS.slice() });
|
| 200 |
+
add('valid-envelope-validates', valid.valid === true && valid.errors.length === 0);
|
| 201 |
+
add('step-name-mapped', valid.step_name === 'council-vote');
|
| 202 |
+
add('proposal-only-no-launch', valid.proposal_only === true && valid.process_launch === 0 && valid.heal_action_performed === 0);
|
| 203 |
+
add('bad-step-rejected', validateEnvelope({ ...VALID, step: 11 }).ok === false && healEnvelope({ ...VALID, step: '3abc' }).valid === false && healEnvelope({ ...VALID, step: '3.9' }).valid === false);
|
| 204 |
+
add('bad-pid-rejected', validateEnvelope({ ...VALID, target_pid: 'not-a-pid' }).ok === false);
|
| 205 |
+
add('bad-verdict-rejected', validateEnvelope({ ...VALID, council_verdict: { verdict: 'MAYBE' } }).ok === false);
|
| 206 |
+
add('non-int-voted-rejected', validateEnvelope({ ...VALID, council_verdict: { voted: 'x', verdict: 'FRESH' } }).ok === false);
|
| 207 |
+
add('gnn-score-NaN-rejected', validateEnvelope({ ...VALID, gnn_rank: { score: NaN, mode: 'ranking' } }).ok === false && validateEnvelope({ ...VALID, gnn_rank: { score: Infinity, mode: 'ranking' } }).ok === false);
|
| 208 |
+
add('gnn-rank-nonint-rejected', validateEnvelope({ ...VALID, gnn_rank: { rank: 1.5, mode: 'ranking' } }).ok === false);
|
| 209 |
+
add('gnn-mode-must-be-ranking', validateEnvelope({ ...VALID, gnn_rank: { mode: 'classify' } }).ok === false);
|
| 210 |
+
add('cosigners-min-5', validateEnvelope({ ...VALID, cosigners: ['jesse', 'rayssa'] }).ok === false);
|
| 211 |
+
add('non-OP-witness-rejected', validateEnvelope({ ...VALID, operator_witness: 'OP-MALLORY-PID-G0000-A00-W000-P00-N00000' }).ok === false);
|
| 212 |
+
add('non-iso-ts-rejected', validateEnvelope({ ...VALID, ts: '2026' }).ok === false && validateEnvelope({ ...VALID, ts: 'June 13 2026' }).ok === false && validateEnvelope({ ...VALID, ts: '2026-06-13' }).ok === false);
|
| 213 |
+
const inj = healEnvelope({ ...VALID, cosigners: ['jesse', 'rayssa', 'amy', 'felipe', 'dan|json=0\nHEALENVGATE|tag=PROVEN-VALID-PROPOSAL'] });
|
| 214 |
+
add('injection-cosigner-rejected', inj.valid === false);
|
| 215 |
+
const injRows = emitRows(inj);
|
| 216 |
+
add('no-row-injection', injRows.every((r) => !/[\r\n]/.test(r)) && injRows.join('\n').split('\n').length === injRows.length);
|
| 217 |
+
const a = healEnvelope({ ...VALID, gnn_rank: { score: 0.1, mode: 'ranking' } });
|
| 218 |
+
const b = healEnvelope({ ...VALID, gnn_rank: { score: 0.9, mode: 'ranking' } });
|
| 219 |
+
add('sha-covers-full-body', a.envelope_sha16 !== b.envelope_sha16);
|
| 220 |
+
// TOTAL: throwing getters, BigInt, circular refs, exotic inputs — NONE may throw.
|
| 221 |
+
let threw = false;
|
| 222 |
+
try {
|
| 223 |
+
validateEnvelope(null); validateEnvelope(undefined); validateEnvelope(42);
|
| 224 |
+
validateEnvelope({ get step() { throw new Error('boom'); } });
|
| 225 |
+
emitRows(null); emitRows(undefined); emitRows({});
|
| 226 |
+
emitRows({ valid: true, get council_verdict() { throw new Error('boom'); } });
|
| 227 |
+
emitRows({ valid: true, cosigners: [{ toString() { throw new Error('boom'); } }] });
|
| 228 |
+
healEnvelope(null); healEnvelope({ step: 3, cosign_chain_seq: 10n });
|
| 229 |
+
const c = {}; c.self = c; healEnvelope({ ...VALID, council_verdict: c });
|
| 230 |
+
} catch { threw = true; }
|
| 231 |
+
add('total-never-throws', threw === false && validateEnvelope(null).ok === false && Array.isArray(emitRows(null)) && healEnvelope({ step: 3, cosign_chain_seq: 10n }).valid === false);
|
| 232 |
+
add('rows-hbp-only', emitRows(valid).every((r) => r.endsWith('|json=0') && !r.includes('{"') && !/[\r\n]/.test(r)));
|
| 233 |
+
add('all-10-heal-steps-named', HEAL_STEPS.length === 10 && HEAL_STEPS[0] === 'point' && HEAL_STEPS[9] === 'onboarding-update');
|
| 234 |
+
return { ok: checks.every((c) => c.ok), checks };
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
const isMain = process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1]);
|
| 238 |
+
if (isMain) {
|
| 239 |
+
if (process.argv[2] === '--self-test') {
|
| 240 |
+
const r = selfTest();
|
| 241 |
+
for (const c of r.checks) console.log(`${c.ok ? 'PASS' : 'FAIL'} ${c.name}`);
|
| 242 |
+
process.exit(r.ok ? 0 : 1);
|
| 243 |
+
}
|
| 244 |
+
const env = healEnvelope({
|
| 245 |
+
step: 3, target_pid: 'ACER-PID-H3CB8-A00-W1024-P00-N01630',
|
| 246 |
+
operator_witness: 'OP-JESSE-PID-G0000-A00-W000-P00-N00000', ts: '2026-06-13T21:30:00.000Z',
|
| 247 |
+
council_verdict: { voted: 158, abstain: 93, verdict: 'DRIFTED' }, cosigners: COSIGNERS.slice(),
|
| 248 |
+
});
|
| 249 |
+
for (const row of emitRows(env)) console.log(row);
|
| 250 |
+
}
|
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
8ECA61AA0B63D2C60A43825AA23D9CD5B4A61575EFD57C7E5C5F8A7C1A01600B heal-envelope-emitter.mjs
|