feat(recall): recall-reliability improvements
Neuron Soul CI / build (pull_request) Failing after 12m52s
Neuron Soul CI / build (pull_request) Failing after 12m52s
- Q1: engram_numeric_valid() guard against non-numeric timestamps in bell scoring - Q2: soul-agnostic cold-start fallback in engram_compile (drops genesis-specific hardcoded node IDs) - Q3: partial-write guard and failure logging in conv_history_persist/load - Q4: document circuit-breaker limitation requiring C runtime support - Q5: println warnings on empty activation/search paths - Q6: load_identity_context warns when all identity fetches return empty - Q7: recall_status state tracking (ok/empty/unavailable) surfaced to LLM via MEMORY STATUS block - Q8: document shared-state race conditions in engram_recall_status and safety_system_addendum - CRITICAL BUG: conv_node_id empty check moved outside is_bell block so silent Conversation node loss is always logged
This commit is contained in:
@@ -148,6 +148,14 @@ fn load_identity_context() -> Void {
|
||||
println("[soul] identity context loaded (" + int_to_str(str_len(ctx)) + " chars, " + int_to_str(parts_count) + " nodes)")
|
||||
}
|
||||
|
||||
// Q6 fix: warn when all three identity node fetches return empty. For genesis this
|
||||
// indicates a corrupted or missing graph. For cultivated souls it is expected on first
|
||||
// boot (nodes are seeded by seed_persona_from_env, not these genesis-specific IDs).
|
||||
// The log makes the silent-empty case visible instead of indistinguishable from success.
|
||||
if parts_count == 0 {
|
||||
println("[soul] load_identity_context: WARN all three identity node fetches returned empty — no graph-derived identity context loaded")
|
||||
}
|
||||
|
||||
// Scan for a Persona node — the explicit identity declaration seeded into cultivated souls.
|
||||
// Stored at seeding time with label "soul:persona" and node_type "Persona".
|
||||
// genesis derives identity from the graph directly; cultivated souls have this node seeded.
|
||||
@@ -162,6 +170,12 @@ fn load_identity_context() -> Void {
|
||||
println("[soul] persona node loaded (" + int_to_str(str_len(p_content)) + " chars)")
|
||||
}
|
||||
}
|
||||
// Q6 fix: if neither identity nodes nor persona node were loaded, log explicitly.
|
||||
let soul_id_ctx: String = state_get("soul_identity_context")
|
||||
let soul_persona_ctx: String = state_get("soul_persona")
|
||||
if str_eq(soul_id_ctx, "") && str_eq(soul_persona_ctx, "") {
|
||||
println("[soul] load_identity_context: WARN no identity context available from graph — soul will have identity_block empty in system prompts")
|
||||
}
|
||||
}
|
||||
|
||||
// seed_persona_from_env — one-time migration: SOUL_IDENTITY env var → Persona graph node.
|
||||
@@ -327,6 +341,15 @@ fn layered_cycle(raw_input: String) -> String {
|
||||
// TODO: wire directly when imprint_respond gains system_override param (imprint.el change).
|
||||
// ISSUE 3 TODO: no semantic crisis detection. Keyword-only means signals that evade
|
||||
// the phrase list pass with zero augmentation. Semantic layer = separate decision.
|
||||
//
|
||||
// Q8 race documentation: "layered_cycle_safety_system_addendum" is a shared process-global
|
||||
// state key. Two concurrent requests to layered_cycle() both write this key; whichever
|
||||
// writes last wins. The concurrent build_system_prompt() read in chat.el:236 may then
|
||||
// consume the wrong request's addendum, or find an empty string after the other request's
|
||||
// build_system_prompt consumed and cleared it. Mitigation: under http_serve_async, the
|
||||
// layered_cycle path and the /api/chat path are different endpoints (typically); true
|
||||
// concurrent layered_cycle calls are uncommon. A robust fix requires per-request state
|
||||
// scoping which needs C runtime support (e.g. a request-id-keyed addendum map).
|
||||
let augmented_addendum: String = safety_augment_system("", raw_input)
|
||||
state_set("layered_cycle_safety_system_addendum", augmented_addendum)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user