8b692e4666
Neuron Soul CI / build (pull_request) Failing after 13m22s
fix(soul): add HTTP-engram guard to safe_to_seed — when ENGRAM_URL is set the HTTP Engram owns persistence; genesis must never save to local snapshot regardless of node counts (was: guard_disk forced to empty string, making the ratio check vacuously true and allowing init_soul_edges+engram_save). fix(soul): use multiplication form for ratio guard — node_count * 16000 < disk_len avoids floor-division truncation that underestimated boundary files (250KB / 16000 = 15.6, floors to 15; a 15-node graph wrongly passed old guard). fix(chat): add safety_augment_system to handle_chat_as_soul, handle_dharma_room_turn, and handle_dharma_room_turn_agentic — all three called the LLM without Hard Bell evaluation, leaving users in dharma rooms without crisis resource routing. fix(neuron-api): add api_persisted read-back to handle_api_define_process — was the only write handler that returned ok:true without verifying the node was actually written to engram. fix(routes): unique temp file path in connectd_post — replaces fixed /tmp/neuron-connectors-req.json with a timestamped path to prevent collision if concurrency is added or two soul instances share a machine. test: add tests/test_bell_safety.el — covers safety_detect_bell_level (none/soft/hard), safety_classify_hard_bell (abuse/self_harm routing), safety_normalize (smart-quote), safety_augment_system, and handle_safety_contact_post (validation + read-back). test: add tests/test_soul_guard.el — pure-function logic tests for the safe_to_seed predicate: 200KB boundary, 47MB/63-node clobber scenario, HTTP-engram mode, multiplication vs division truncation at 250KB. test: add tests/test_api_define_process.el — verifies the define_process write is read-back verified after the fix.
125 lines
5.9 KiB
EmacsLisp
125 lines
5.9 KiB
EmacsLisp
// ── tests/test_soul_guard.el ──────────────────────────────────────────────────
|
|
//
|
|
// Logic tests for the genesis guard in soul.el (feat/connectors-soul).
|
|
//
|
|
// The guard is top-level imperative boot code. This file tests the predicate
|
|
// logic as pure functions to verify the conditions exhaustively:
|
|
//
|
|
// safe_to_seed = !using_http_engram &&
|
|
// !(guard_disk_len > 200000 && loaded_nodes * 16000 < guard_disk_len)
|
|
//
|
|
// Scenarios:
|
|
// - Boundary: 199,999 bytes + sparse -> safe_to_seed == true
|
|
// - Boundary: 200,001 bytes + sparse -> safe_to_seed == false
|
|
// - Ratio: 47MB + 63 nodes -> false (the 2026-06-14 clobber scenario)
|
|
// - HTTP mode -> false unconditionally
|
|
// - Multiplication form vs old division form near 250KB boundary
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
let pass_count: Int = 0
|
|
let fail_count: Int = 0
|
|
|
|
fn assert_eq_bool(label: String, got: Bool, expected: Bool) -> Void {
|
|
let got_s: String = if got { "true" } else { "false" }
|
|
let exp_s: String = if expected { "true" } else { "false" }
|
|
if str_eq(got_s, exp_s) {
|
|
let pass_count = pass_count + 1
|
|
println(" PASS: " + label)
|
|
} else {
|
|
let fail_count = fail_count + 1
|
|
println(" FAIL: " + label)
|
|
println(" got: " + got_s)
|
|
println(" expected: " + exp_s)
|
|
}
|
|
}
|
|
|
|
// guard_predicate — mirrors the safe_to_seed expression in soul.el exactly.
|
|
fn guard_predicate(using_http: Bool, disk_len: Int, loaded_nodes: Int) -> Bool {
|
|
if using_http { return false }
|
|
let ratio_block: Bool = disk_len > 200000 && loaded_nodes * 16000 < disk_len
|
|
return !ratio_block
|
|
}
|
|
|
|
// ── Section 1: 200KB boundary ─────────────────────────────────────────────────
|
|
|
|
println("")
|
|
println("1. guard boundary — 199,999 bytes + sparse load -> safe_to_seed true")
|
|
|
|
let safe_below: Bool = guard_predicate(false, 199999, 1)
|
|
assert_eq_bool("199,999 bytes + 1 node -> safe", safe_below, true)
|
|
|
|
let safe_below_zero: Bool = guard_predicate(false, 199999, 0)
|
|
assert_eq_bool("199,999 bytes + 0 nodes -> safe (below 200KB threshold)", safe_below_zero, true)
|
|
|
|
println("")
|
|
println("2. guard boundary — 200,001 bytes + sparse load -> safe_to_seed false")
|
|
|
|
let unsafe_above: Bool = guard_predicate(false, 200001, 1)
|
|
assert_eq_bool("200,001 bytes + 1 node -> unsafe", unsafe_above, false)
|
|
|
|
let unsafe_zero: Bool = guard_predicate(false, 200001, 0)
|
|
assert_eq_bool("200,001 bytes + 0 nodes -> unsafe", unsafe_zero, false)
|
|
|
|
// ── Section 2: ratio guard — 47MB + 63 nodes ─────────────────────────────────
|
|
|
|
println("")
|
|
println("3. guard ratio — 47MB + 63 nodes (the 2026-06-14 clobber scenario)")
|
|
|
|
let clobber_blocked: Bool = guard_predicate(false, 47000000, 63)
|
|
assert_eq_bool("47MB + 63 nodes -> unsafe (clobber blocked)", clobber_blocked, false)
|
|
|
|
// 47MB / 16000 = 2937.5 -> need >= 2938 nodes for safe
|
|
let clobber_safe: Bool = guard_predicate(false, 47000000, 2938)
|
|
assert_eq_bool("47MB + 2938 nodes -> safe (load correct)", clobber_safe, true)
|
|
|
|
let boundary_blocked: Bool = guard_predicate(false, 47000000, 2937)
|
|
assert_eq_bool("47MB + 2937 nodes -> unsafe (just below ratio)", boundary_blocked, false)
|
|
|
|
// ── Section 3: HTTP-engram mode — always false ────────────────────────────────
|
|
|
|
println("")
|
|
println("4. guard HTTP mode — always false regardless of disk/node counts")
|
|
|
|
let http_zero: Bool = guard_predicate(true, 0, 0)
|
|
assert_eq_bool("HTTP mode + 0/0 -> unsafe", http_zero, false)
|
|
|
|
let http_small: Bool = guard_predicate(true, 1000, 100)
|
|
assert_eq_bool("HTTP mode + small snapshot -> unsafe", http_small, false)
|
|
|
|
let http_large: Bool = guard_predicate(true, 47000000, 2938)
|
|
assert_eq_bool("HTTP mode + large/fully-loaded -> unsafe", http_large, false)
|
|
|
|
// ── Section 4: normal local mode — small/fresh snapshots ─────────────────────
|
|
|
|
println("")
|
|
println("5. guard normal local mode — small/fresh snapshots")
|
|
|
|
let fresh_genesis: Bool = guard_predicate(false, 0, 0)
|
|
assert_eq_bool("fresh genesis (0 bytes, 0 nodes) -> safe", fresh_genesis, true)
|
|
|
|
let small_snapshot: Bool = guard_predicate(false, 50000, 5)
|
|
assert_eq_bool("50KB + 5 nodes -> safe (below 200KB threshold)", small_snapshot, true)
|
|
|
|
// ── Section 5: multiplication vs division — 250KB boundary ───────────────────
|
|
|
|
println("")
|
|
println("6. guard multiplication form — avoids floor-division truncation at 250KB")
|
|
|
|
// OLD (division): 250000 / 16000 = 15 (floors 15.625). 15 < 15 is false -> wrongly safe.
|
|
// NEW (multiplication): 15 * 16000 = 240000 < 250000 -> correctly unsafe.
|
|
let div_boundary: Bool = guard_predicate(false, 250000, 15)
|
|
assert_eq_bool("250,000 bytes + 15 nodes -> unsafe (multiplication form)", div_boundary, false)
|
|
|
|
// With 16 nodes: 16 * 16000 = 256000 > 250000 -> safe.
|
|
let div_just_enough: Bool = guard_predicate(false, 250000, 16)
|
|
assert_eq_bool("250,000 bytes + 16 nodes -> safe", div_just_enough, true)
|
|
|
|
// Exact equality: disk_len == node_count * 16000 -> not sparse -> safe.
|
|
let exact_match: Bool = guard_predicate(false, 32000, 2)
|
|
assert_eq_bool("exact ratio (32000 bytes, 2 nodes: 2*16000=32000) -> safe", exact_match, true)
|
|
|
|
// ── Summary ───────────────────────────────────────────────────────────────────
|
|
|
|
println("")
|
|
println("soul_guard tests: " + int_to_str(pass_count) + " passed, " + int_to_str(fail_count) + " failed")
|