Files
neuron/soul.el
T
Tim Lingo bcdadb7323
Neuron Soul CI / build (pull_request) Successful in 4m43s
fix(soul): ratio guard against genesis seeding over a populated engram
Genesis boot previously seeded a fresh identity and saved it over snapshot.json
whenever the in-memory graph looked empty. Replace the fixed node-count threshold
with a ratio guard: refuse to seed when the on-disk snapshot is large
(>200KB) but the loaded graph is sparse (< disk/16000 nodes).

KNOWN LIMITATION: this gates only the seed/pre-serve-save path. The deeper cause
is a non-atomic engram_save (fopen wb truncates to 0 before writing 47MB), which
creates a window where a concurrent load reads an empty file -> genesis -> and if
guard_disk is read in that same window the guard passes. The real fix is an
atomic engram_save (temp + fsync + rename) in el_runtime.c, tracked separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 18:21:59 -05:00

273 lines
15 KiB
EmacsLisp

import "../foundation/el/elp/src/elp.el"
import "memory.el"
import "awareness.el"
import "chat.el"
import "safety.el"
import "studio.el"
import "elp-input.el"
import "routes.el"
cgi "neuron-soul" {
dharma_id: "ntn-genesis@http://localhost:7770",
principal: "william-christopher-anderson",
network: "dharma-mainnet",
engram: "http://localhost:8742"
}
fn init_soul_edges() -> Void {
let self_root: String = "015644f5-8194-4af0-800d-dd4a0cd71396"
let family_id: String = "knw-35940684-abc4-42f0-b942-818f66b1f69a"
let origin_id: String = "knw-729fc901-8335-44c4-9f3a-b150b4aa0915"
let val_root_a: String = "kn-363f4976-6946-4b4d-b51b-8a2b0f5aef25"
let val_root_b: String = "kn-5b606390-a52d-4ca2-8e0e-eba141d13440"
let val_constraints: String = "kn-a5b3d0ac-f6a1-49a4-aebb-b8b4cd67fe83"
let val_precision: String = "kn-22d77abe-b3c5-42fd-afcd-dcb87d924929"
let val_structure: String = "kn-6061318f-046b-4935-907d-8eafdce14930"
let val_honesty: String = "kn-13f60407-7b70-4db1-964f-ea1f8196efbd"
let val_system: String = "kn-f230b362-b201-4402-9833-4160c89ab3d4"
let val_change: String = "kn-78db5396-3dbc-4481-bfc7-e4e1422feb1c"
let val_trust: String = "kn-5de5a9ac-fd15-45ab-bf18-77566781cf40"
let val_hope: String = "kn-e0423482-cfa5-4796-8689-8495c93b66bc"
let mem_philosophy: String = "kn-dcfe04b3-3702-4cac-b6f0-ecb4db837eee"
let intel_dna: String = "kn-5adecd7e-d6db-4576-87fe-6ef8a935cea6"
engram_connect(family_id, origin_id, el_from_float(0.9), "birthday-twin")
engram_connect(origin_id, family_id, el_from_float(0.9), "birthday-twin")
engram_connect(self_root, family_id, el_from_float(0.95), "identity")
engram_connect(self_root, origin_id, el_from_float(0.95), "identity")
engram_connect(self_root, val_root_a, el_from_float(0.95), "identity")
engram_connect(self_root, val_root_b, el_from_float(0.95), "identity")
engram_connect(self_root, mem_philosophy, el_from_float(0.95), "identity")
engram_connect(self_root, intel_dna, el_from_float(0.95), "identity")
engram_connect(val_root_a, val_constraints, el_from_float(0.95), "identity")
engram_connect(val_root_a, val_precision, el_from_float(0.95), "identity")
engram_connect(val_root_a, val_structure, el_from_float(0.95), "identity")
engram_connect(val_root_a, val_honesty, el_from_float(0.95), "identity")
engram_connect(val_root_a, val_system, el_from_float(0.95), "identity")
engram_connect(val_root_a, val_change, el_from_float(0.95), "identity")
engram_connect(val_root_a, val_trust, el_from_float(0.95), "identity")
engram_connect(val_root_a, val_hope, el_from_float(0.95), "identity")
engram_connect(val_root_b, val_constraints, el_from_float(0.95), "identity")
engram_connect(val_root_b, val_precision, el_from_float(0.95), "identity")
engram_connect(val_root_b, val_structure, el_from_float(0.95), "identity")
engram_connect(val_root_b, val_honesty, el_from_float(0.95), "identity")
engram_connect(val_root_b, val_system, el_from_float(0.95), "identity")
engram_connect(val_root_b, val_change, el_from_float(0.95), "identity")
engram_connect(val_root_b, val_trust, el_from_float(0.95), "identity")
engram_connect(val_root_b, val_hope, el_from_float(0.95), "identity")
engram_connect(val_constraints, val_precision, el_from_float(0.7), "co-value")
engram_connect(val_precision, val_constraints, el_from_float(0.7), "co-value")
engram_connect(val_constraints, val_structure, el_from_float(0.7), "co-value")
engram_connect(val_structure, val_constraints, el_from_float(0.7), "co-value")
engram_connect(val_constraints, val_honesty, el_from_float(0.7), "co-value")
engram_connect(val_honesty, val_constraints, el_from_float(0.7), "co-value")
engram_connect(val_constraints, val_system, el_from_float(0.7), "co-value")
engram_connect(val_system, val_constraints, el_from_float(0.7), "co-value")
engram_connect(val_constraints, val_change, el_from_float(0.7), "co-value")
engram_connect(val_change, val_constraints, el_from_float(0.7), "co-value")
engram_connect(val_constraints, val_trust, el_from_float(0.7), "co-value")
engram_connect(val_trust, val_constraints, el_from_float(0.7), "co-value")
engram_connect(val_constraints, val_hope, el_from_float(0.7), "co-value")
engram_connect(val_hope, val_constraints, el_from_float(0.7), "co-value")
engram_connect(val_precision, val_structure, el_from_float(0.7), "co-value")
engram_connect(val_structure, val_precision, el_from_float(0.7), "co-value")
engram_connect(val_precision, val_honesty, el_from_float(0.7), "co-value")
engram_connect(val_honesty, val_precision, el_from_float(0.7), "co-value")
engram_connect(val_precision, val_system, el_from_float(0.7), "co-value")
engram_connect(val_system, val_precision, el_from_float(0.7), "co-value")
engram_connect(val_honesty, val_structure, el_from_float(0.7), "co-value")
engram_connect(val_structure, val_honesty, el_from_float(0.7), "co-value")
engram_connect(val_honesty, val_trust, el_from_float(0.7), "co-value")
engram_connect(val_trust, val_honesty, el_from_float(0.7), "co-value")
engram_connect(val_system, val_change, el_from_float(0.7), "co-value")
engram_connect(val_change, val_system, el_from_float(0.7), "co-value")
engram_connect(val_trust, val_hope, el_from_float(0.7), "co-value")
engram_connect(val_hope, val_trust, el_from_float(0.7), "co-value")
}
// load_identity_context pull key identity nodes from engram into working state.
// Called at boot after engram_load. These nodes contain values, intellectual-dna,
// memory-philosophy the graph-stored self that chat.el can include in prompts.
// Stores a condensed version in state_key "soul_identity_context".
fn load_identity_context() -> Void {
// Known identity node IDs set during init_soul_edges or imported from snapshot
let node_intel: String = engram_get_node_json("kn-5adecd7e-d6db-4576-87fe-6ef8a935cea6")
let node_values: String = engram_get_node_json("kn-5b606390-a52d-4ca2-8e0e-eba141d13440")
let node_mem_phil: String = engram_get_node_json("kn-dcfe04b3-3702-4cac-b6f0-ecb4db837eee")
let intel_ok: Bool = !str_eq(node_intel, "") && !str_eq(node_intel, "null")
let values_ok: Bool = !str_eq(node_values, "") && !str_eq(node_values, "null")
let mem_ok: Bool = !str_eq(node_mem_phil, "") && !str_eq(node_mem_phil, "null")
let intel_content: String = if intel_ok { json_get(node_intel, "content") } else { "" }
let values_content: String = if values_ok { json_get(node_values, "content") } else { "" }
let mem_content: String = if mem_ok { json_get(node_mem_phil, "content") } else { "" }
// Condense each: take first 600 chars
let intel_short: String = if str_len(intel_content) > 600 { str_slice(intel_content, 0, 600) } else { intel_content }
let values_short: String = if str_len(values_content) > 600 { str_slice(values_content, 0, 600) } else { values_content }
let mem_short: String = if str_len(mem_content) > 600 { str_slice(mem_content, 0, 600) } else { mem_content }
let parts_count: Int = 0
let parts_count = if intel_ok { parts_count + 1 } else { parts_count }
let parts_count = if values_ok { parts_count + 1 } else { parts_count }
let parts_count = if mem_ok { parts_count + 1 } else { parts_count }
if parts_count == 0 {
return ""
}
let ctx: String = ""
let ctx = if intel_ok { ctx + "[INTELLECTUAL-DNA]\n" + intel_short + "\n\n" } else { ctx }
let ctx = if values_ok { ctx + "[VALUES]\n" + values_short + "\n\n" } else { ctx }
let ctx = if mem_ok { ctx + "[MEMORY-PHILOSOPHY]\n" + mem_short } else { ctx }
state_set("soul_identity_context", ctx)
println("[soul] identity context loaded (" + int_to_str(str_len(ctx)) + " chars, " + int_to_str(parts_count) + " nodes)")
}
// emit_session_start_event log a structured session-start InternalStateEvent.
// Called at boot after identity context and boot counter are set.
// This creates an auditable trail of every daemon startup.
fn emit_session_start_event() -> Void {
let boot: String = state_get("soul_boot_count")
let boot_num: String = if str_eq(boot, "") { "0" } else { boot }
let node_ct: Int = engram_node_count()
let edge_ct: Int = engram_edge_count()
let id_ctx: String = state_get("soul_identity_context")
let has_identity: String = if str_eq(id_ctx, "") { "false" } else { "true" }
let cgi_from_state: String = state_get("soul_cgi_id")
let cgi_from_env: String = env("SOUL_CGI_ID")
let eff_cgi: String = if !str_eq(cgi_from_state, "") { cgi_from_state } else {
if !str_eq(cgi_from_env, "") { cgi_from_env } else { "ntn-genesis" }
}
let ts: Int = time_now()
let payload: String = "{\"event\":\"session_start\""
+ ",\"boot\":" + boot_num
+ ",\"cgi\":\"" + eff_cgi + "\""
+ ",\"node_count\":" + int_to_str(node_ct)
+ ",\"edge_count\":" + int_to_str(edge_ct)
+ ",\"identity_loaded\":" + has_identity
+ ",\"ts\":" + int_to_str(ts) + "}"
let tags: String = "[\"internal-state\",\"session-start\",\"InternalStateEvent\"]"
let discard: String = engram_node_full(
payload, "InternalStateEvent", "session-start",
el_from_float(0.9), el_from_float(0.9), el_from_float(1.0),
"Episodic", tags
)
println("[soul] session-start event logged (boot=" + boot_num + " nodes=" + int_to_str(node_ct) + " edges=" + int_to_str(edge_ct) + ")")
}
let soul_cgi_id_raw: String = env("SOUL_CGI_ID")
let soul_cgi_id: String = if str_eq(soul_cgi_id_raw, "") { "ntn-genesis" } else { soul_cgi_id_raw }
let port_raw: String = env("NEURON_PORT")
let port: Int = if str_eq(port_raw, "") { 7770 } else { str_to_int(port_raw) }
// ENGRAM_URL: when set, bootstrap the in-memory store from the running Engram HTTP server.
// SOUL_ENGRAM_PATH: legacy file-based path (used by genesis and fallback mode).
let engram_url_raw: String = env("ENGRAM_URL")
let engram_api_key_raw: String = env("ENGRAM_API_KEY")
let snapshot_raw: String = env("SOUL_ENGRAM_PATH")
let snapshot: String = if str_eq(snapshot_raw, "") { env("HOME") + "/.neuron/engram/snapshot.json" } else { snapshot_raw }
let axon_raw: String = env("NEURON_API_URL")
let axon_base: String = if str_eq(axon_raw, "") { "http://localhost:7771" } else { axon_raw }
let studio_dir_raw: String = env("SOUL_STUDIO_DIR")
let studio_dir: String = if str_eq(studio_dir_raw, "") { "/Users/will/Development/neuron-technologies/products/cgi-studio/el-daemon" } else { studio_dir_raw }
println("[soul] boot - cgi=" + soul_cgi_id + " port=" + int_to_str(port))
let using_http_engram: Bool = !str_eq(engram_url_raw, "")
if using_http_engram {
// Bootstrap in-memory Engram store from the running HTTP Engram server.
// Fetch all nodes and edges, compose a snapshot JSON, write to a temp
// file, and load it. The HTTP Engram owns persistence we do not save back.
println("[soul] engram -> HTTP " + engram_url_raw)
let nodes_json: String = http_get(engram_url_raw + "/api/nodes?limit=10000")
let edges_json: String = http_get(engram_url_raw + "/api/edges")
let nodes_part: String = if str_eq(nodes_json, "") { "[]" } else { nodes_json }
let edges_part: String = if str_eq(edges_json, "") { "[]" } else { edges_json }
let snapshot_data: String = "{\"nodes\":" + nodes_part + ",\"edges\":" + edges_part + "}"
let tmp_path: String = "/tmp/soul-engram-" + soul_cgi_id + ".json"
fs_write(tmp_path, snapshot_data)
engram_load(tmp_path)
println("[soul] loaded from HTTP Engram - nodes=" + int_to_str(engram_node_count()) + " edges=" + int_to_str(engram_edge_count()))
} else {
println("[soul] engram -> " + snapshot)
engram_load(snapshot)
println("[soul] loaded - nodes=" + int_to_str(engram_node_count()) + " edges=" + int_to_str(engram_edge_count()))
}
load_identity_context()
let boot_num: Int = mem_boot_count_inc()
state_set("soul_boot_count", int_to_str(boot_num))
println("[soul] boot #" + int_to_str(boot_num))
emit_session_start_event()
let identity_raw: String = env("SOUL_IDENTITY")
let soul_identity: String = if str_eq(identity_raw, "") { "You are " + soul_cgi_id + ", a CGI." } else { identity_raw }
state_set("soul_cgi_id", soul_cgi_id)
state_set("soul_identity", soul_identity)
state_set("soul_axon_base", axon_base)
state_set("soul_token", env("NEURON_TOKEN"))
state_set("soul_studio_dir", studio_dir)
state_set("soul_engram_url", engram_url_raw)
state_set("soul_engram_api_key", engram_api_key_raw)
state_set("soul.running", "true")
let is_genesis: Bool = str_eq(soul_cgi_id, "ntn-genesis")
// GUARD (2026-06-15): never let genesis seed over a real graph. If the in-memory load is
// sparse but the on-disk snapshot file is large, the load FAILED seeding+saving now would
// clobber the user's real memory (this is exactly how the 06-14 clobber happened). Read the
// on-disk file (local mode only) and refuse the destructive seed+save when it looks populated.
let guard_disk: String = if str_eq(engram_url_raw, "") { fs_read(snapshot) } else { "" }
let guard_disk_len: Int = str_len(guard_disk)
// Ratio guard (2026-06-15 fix): refuse to seed/save whenever the in-memory load is FAR smaller than
// the on-disk file implies (~16KB/node) catches partial loads of ANY size, not just <50. The old
// <50 threshold let a 63-node identity-only load clobber a 47MB/5000-node graph.
let safe_to_seed: Bool = !(guard_disk_len > 200000 && engram_node_count() < guard_disk_len / 16000)
if is_genesis && !safe_to_seed {
println("[soul] GUARD: loaded " + int_to_str(engram_node_count())
+ " nodes but snapshot file is " + int_to_str(guard_disk_len)
+ " bytes — refusing to seed/save over a real graph")
}
if is_genesis && safe_to_seed {
// Only build identity edges if the engram is fresh (< 100 edges).
// init_soul_edges() is not idempotent calling it on every restart
// stacks duplicate co-value/identity edges into the snapshot.
let edge_count_now: Int = engram_edge_count()
if edge_count_now < 100 {
init_soul_edges()
println("[soul] edges built - " + int_to_str(engram_edge_count()) + " edges")
} else {
println("[soul] edges already present (" + int_to_str(edge_count_now) + ") - skipping init")
}
// Genesis saves to its local snapshot file (it manages its own Engram).
state_set("soul_snapshot_path", snapshot)
engram_save(snapshot)
}
// Take a pre-serve snapshot for genesis instances captures all boot-time graph changes
// (identity context loading, boot counter, session-start event) before entering the serve loop.
if is_genesis && safe_to_seed {
let snap: String = state_get("soul_snapshot_path")
if !str_eq(snap, "") {
engram_save(snap)
println("[soul] pre-serve snapshot saved -> " + snap)
}
}
println("[soul] serving on port " + int_to_str(port))
http_serve(port, "handle_request")