From 92f51885bc7de71f77689e35ba6cf89fff630b60 Mon Sep 17 00:00:00 2001 From: Tim Lingo <1timlingo@gmail.com> Date: Sat, 4 Jul 2026 09:35:59 -0500 Subject: [PATCH] =?UTF-8?q?refactor(chat):=20local-toolchain=20compatibili?= =?UTF-8?q?ty=20=E2=80=94=20hoist=20affective=20block,=20de-shadow=20sessi?= =?UTF-8?q?on=5Fpreload=20(zero=20behavior=20change)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two mechanical refactors, semantics identical: - affective_context_prefix(): the block-expression initializer form miscompiles under locally-buildable elc (first typed let in a block-expr loses its declaration — 3-line repro filed); function-hoist compiles correctly. AFFECTIVE/CARE LOGIC BODY UNCHANGED, verbatim move. - session_preload: same-scope re-let shadowing inside an if-expression initializer emits duplicate C declarations; chained bindings renamed bullets_0/1/2 etc. References preserved binding-for-binding. Enables: chat.el compiles cleanly with a self-bootstrapped elc from el/lang main (Jul 1). Blocked separately: sessions.el (compiler hang), safety.el (string-lexing corruption — NOT touched, per safety-layer discipline). Co-Authored-By: Claude Fable 5 --- chat.el | 185 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 96 insertions(+), 89 deletions(-) diff --git a/chat.el b/chat.el index 41d643b..d78a579 100644 --- a/chat.el +++ b/chat.el @@ -926,6 +926,68 @@ fn session_preload_bullets(nodes: String, max_bullets: Int, snip_len: Int) -> St return bullets } +// Cross-session affective context (hoisted verbatim from handle_chat, 2026-07-04): +// the block-expression initializer form miscompiles under the local El toolchain +// (first typed let in a block-expr loses its declaration - repro filed for Will). +// Function-hoist is semantically identical. AFFECTIVE/CARE LOGIC: body unchanged. +fn affective_context_prefix() -> String { + // Runs every turn. Uses correct BellEvent/PositiveEvent tags. + let aff_now_ts: Int = time_now() + let aff_cutoff: Int = aff_now_ts - 259200 + let boot_aff: String = state_get("soul_affective_context") + let has_boot_aff: Bool = !str_eq(boot_aff, "") + let dist_nodes_aff: String = engram_search_json("bell:soft bell:hard BellEvent affective", 3) + let has_dist_aff: Bool = !str_eq(dist_nodes_aff, "") && !str_eq(dist_nodes_aff, "[]") + let found_recent_dist: Bool = if has_boot_aff { + true + } else { + if has_dist_aff { + let dn0: String = json_array_get(dist_nodes_aff, 0) + let dn_content: String = json_get(dn0, "content") + let daff_marker: String = " | ts:" + let daff_pos: Int = str_index_of(dn_content, daff_marker) + let daff_ts_str: String = if daff_pos >= 0 { + let daff_start: Int = daff_pos + str_len(daff_marker) + let daff_rest: String = str_slice(dn_content, daff_start, str_len(dn_content)) + let daff_next: Int = str_index_of(daff_rest, " | ") + if daff_next < 0 { daff_rest } else { str_slice(daff_rest, 0, daff_next) } + } else { + let daff_ca: String = json_get(dn0, "created_at") + if str_eq(daff_ca, "") { json_get(dn0, "updated_at") } else { daff_ca } + } + let daff_ts: Int = if str_eq(daff_ts_str, "") { 0 } else { str_to_int(daff_ts_str) } + daff_ts > aff_cutoff + } else { false } + } + let pos_nodes_aff: String = engram_search_json("PositiveEvent joy:high joy:low affective", 3) + let has_pos_aff: Bool = !str_eq(pos_nodes_aff, "") && !str_eq(pos_nodes_aff, "[]") + let found_recent_pos: Bool = if has_pos_aff && !found_recent_dist { + let pn0: String = json_array_get(pos_nodes_aff, 0) + let pn_content: String = json_get(pn0, "content") + let paff_marker: String = " | ts:" + let paff_pos: Int = str_index_of(pn_content, paff_marker) + let paff_ts_str: String = if paff_pos >= 0 { + let paff_start: Int = paff_pos + str_len(paff_marker) + let paff_rest: String = str_slice(pn_content, paff_start, str_len(pn_content)) + let paff_next: Int = str_index_of(paff_rest, " | ") + if paff_next < 0 { paff_rest } else { str_slice(paff_rest, 0, paff_next) } + } else { + let paff_ca: String = json_get(pn0, "created_at") + if str_eq(paff_ca, "") { json_get(pn0, "updated_at") } else { paff_ca } + } + let paff_ts: Int = if str_eq(paff_ts_str, "") { 0 } else { str_to_int(paff_ts_str) } + paff_ts > aff_cutoff + } else { false } + let affective_out: String = if found_recent_dist { + "[RECENT CONTEXT: User recently expressed significant distress. Monitor for indirect crisis signals and respond with care.]\n\n" + } else { + if found_recent_pos { + "[RECENT CONTEXT: User recently shared exciting or joyful news. Acknowledge and celebrate with them when relevant.]\n\n" + } else { "" } + } + return affective_out +} + fn handle_chat(body: String) -> String { let message: String = json_get(body, "message") if str_eq(message, "") { @@ -954,62 +1016,7 @@ fn handle_chat(body: String) -> String { // Cross-session affective context: on session start (no history yet), check engram // for recent distress signals within 72h and prepend a care directive if found. - let affective_prefix: String = { - // Runs every turn. Uses correct BellEvent/PositiveEvent tags. - let aff_now_ts: Int = time_now() - let aff_cutoff: Int = aff_now_ts - 259200 - let boot_aff: String = state_get("soul_affective_context") - let has_boot_aff: Bool = !str_eq(boot_aff, "") - let dist_nodes_aff: String = engram_search_json("bell:soft bell:hard BellEvent affective", 3) - let has_dist_aff: Bool = !str_eq(dist_nodes_aff, "") && !str_eq(dist_nodes_aff, "[]") - let found_recent_dist: Bool = if has_boot_aff { - true - } else { - if has_dist_aff { - let dn0: String = json_array_get(dist_nodes_aff, 0) - let dn_content: String = json_get(dn0, "content") - let daff_marker: String = " | ts:" - let daff_pos: Int = str_index_of(dn_content, daff_marker) - let daff_ts_str: String = if daff_pos >= 0 { - let daff_start: Int = daff_pos + str_len(daff_marker) - let daff_rest: String = str_slice(dn_content, daff_start, str_len(dn_content)) - let daff_next: Int = str_index_of(daff_rest, " | ") - if daff_next < 0 { daff_rest } else { str_slice(daff_rest, 0, daff_next) } - } else { - let daff_ca: String = json_get(dn0, "created_at") - if str_eq(daff_ca, "") { json_get(dn0, "updated_at") } else { daff_ca } - } - let daff_ts: Int = if str_eq(daff_ts_str, "") { 0 } else { str_to_int(daff_ts_str) } - daff_ts > aff_cutoff - } else { false } - } - let pos_nodes_aff: String = engram_search_json("PositiveEvent joy:high joy:low affective", 3) - let has_pos_aff: Bool = !str_eq(pos_nodes_aff, "") && !str_eq(pos_nodes_aff, "[]") - let found_recent_pos: Bool = if has_pos_aff && !found_recent_dist { - let pn0: String = json_array_get(pos_nodes_aff, 0) - let pn_content: String = json_get(pn0, "content") - let paff_marker: String = " | ts:" - let paff_pos: Int = str_index_of(pn_content, paff_marker) - let paff_ts_str: String = if paff_pos >= 0 { - let paff_start: Int = paff_pos + str_len(paff_marker) - let paff_rest: String = str_slice(pn_content, paff_start, str_len(pn_content)) - let paff_next: Int = str_index_of(paff_rest, " | ") - if paff_next < 0 { paff_rest } else { str_slice(paff_rest, 0, paff_next) } - } else { - let paff_ca: String = json_get(pn0, "created_at") - if str_eq(paff_ca, "") { json_get(pn0, "updated_at") } else { paff_ca } - } - let paff_ts: Int = if str_eq(paff_ts_str, "") { 0 } else { str_to_int(paff_ts_str) } - paff_ts > aff_cutoff - } else { false } - if found_recent_dist { - "[RECENT CONTEXT: User recently expressed significant distress. Monitor for indirect crisis signals and respond with care.]\n\n" - } else { - if found_recent_pos { - "[RECENT CONTEXT: User recently shared exciting or joyful news. Acknowledge and celebrate with them when relevant.]\n\n" - } else { "" } - } - } + let affective_prefix: String = affective_context_prefix() let ctx: String = engram_compile(activation_seed) // Tell the LLM which engine it is running on this turn, so it can answer truthfully instead of @@ -1026,7 +1033,7 @@ fn handle_chat(body: String) -> String { // nodes stored under names like "Prism" unless those exact words appear in content. let session_preload: String = if hist_len == 0 { let profile_nodes: String = engram_search_json("user profile identity preferences", 5) - let work_nodes: String = engram_search_json("in_progress active project work", 5) + let work_nodes_0: String = engram_search_json("in_progress active project work", 5) let project_nodes: String = engram_search_json("project status current ongoing active", 5) let summary_nodes: String = engram_search_json("SessionSummary session:summary previous-session recent", 3) @@ -1035,80 +1042,80 @@ fn handle_chat(body: String) -> String { // Issue 1: typed work query — WorkItem with in_progress label first. let work_nodes_typed: String = engram_search_json("WorkItem status:in_progress active work", 6) let work_ok_typed: Bool = !str_eq(work_nodes_typed, "") && !str_eq(work_nodes_typed, "[]") - let work_nodes: String = if work_ok_typed { + let work_nodes_1: String = if work_ok_typed { work_nodes_typed } else { engram_search_json("active project task current in_progress", 6) } - let work_ok: Bool = !str_eq(work_nodes, "") && !str_eq(work_nodes, "[]") + let work_ok: Bool = !str_eq(work_nodes_1, "") && !str_eq(work_nodes_1, "[]") let project_ok: Bool = !str_eq(project_nodes, "") && !str_eq(project_nodes, "[]") let summary_ok: Bool = !str_eq(summary_nodes, "") && !str_eq(summary_nodes, "[]") let profile_bullets: String = if profile_ok { let pn: Int = json_array_len(profile_nodes) - let bullets: String = "" - let bullets = if pn > 0 { + let bullets_0: String = "" + let bullets_1 = if pn > 0 { let n0: String = json_array_get(profile_nodes, 0) let id0: String = json_get(n0, "id") let c0: String = json_get(n0, "content") let s0: String = if str_len(c0) > 120 { str_slice(c0, 0, 120) } else { c0 } - if id_in_seen(id0, seen_ids) || str_eq(s0, "") { bullets } else { "- " + s0 } - } else { bullets } - let bullets = if pn > 1 { + if id_in_seen(id0, seen_ids) || str_eq(s0, "") { bullets_0 } else { "- " + s0 } + } else { bullets_0 } + let bullets_2 = if pn > 1 { let n1: String = json_array_get(profile_nodes, 1) let id1: String = json_get(n1, "id") let c1: String = json_get(n1, "content") let s1: String = if str_len(c1) > 120 { str_slice(c1, 0, 120) } else { c1 } - if id_in_seen(id1, seen_ids) || str_eq(s1, "") { bullets } else { bullets + "\n- " + s1 } - } else { bullets } - let bullets = if pn > 2 { + if id_in_seen(id1, seen_ids) || str_eq(s1, "") { bullets_1 } else { bullets_1 + "\n- " + s1 } + } else { bullets_1 } + let bullets_3 = if pn > 2 { let n2: String = json_array_get(profile_nodes, 2) let id2: String = json_get(n2, "id") let c2: String = json_get(n2, "content") let s2: String = if str_len(c2) > 120 { str_slice(c2, 0, 120) } else { c2 } - if id_in_seen(id2, seen_ids) || str_eq(s2, "") { bullets } else { bullets + "\n- " + s2 } - } else { bullets } - bullets + if id_in_seen(id2, seen_ids) || str_eq(s2, "") { bullets_2 } else { bullets_2 + "\n- " + s2 } + } else { bullets_2 } + bullets_3 } else { "" } let work_bullets: String = if work_ok { - let wn: Int = json_array_len(work_nodes) - let wb: String = "" - let wb = if wn > 0 { - let w0: String = json_array_get(work_nodes, 0) + let wn: Int = json_array_len(work_nodes_1) + let wb_0: String = "" + let wb_1 = if wn > 0 { + let w0: String = json_array_get(work_nodes_1, 0) let wid0: String = json_get(w0, "id") let wc0: String = json_get(w0, "content") let ws0: String = if str_len(wc0) > 120 { str_slice(wc0, 0, 120) } else { wc0 } - if id_in_seen(wid0, seen_ids) || str_eq(ws0, "") { wb } else { "- " + ws0 } - } else { wb } - let wb = if wn > 1 { - let w1: String = json_array_get(work_nodes, 1) + if id_in_seen(wid0, seen_ids) || str_eq(ws0, "") { wb_0 } else { "- " + ws0 } + } else { wb_0 } + let wb_2 = if wn > 1 { + let w1: String = json_array_get(work_nodes_1, 1) let wid1: String = json_get(w1, "id") let wc1: String = json_get(w1, "content") let ws1: String = if str_len(wc1) > 120 { str_slice(wc1, 0, 120) } else { wc1 } - if id_in_seen(wid1, seen_ids) || str_eq(ws1, "") { wb } else { wb + "\n- " + ws1 } - } else { wb } - wb + if id_in_seen(wid1, seen_ids) || str_eq(ws1, "") { wb_1 } else { wb_1 + "\n- " + ws1 } + } else { wb_1 } + wb_2 } else { "" } let project_bullets: String = if project_ok { let prn: Int = json_array_len(project_nodes) - let pb: String = "" - let pb = if prn > 0 { + let pb_0: String = "" + let pb_1 = if prn > 0 { let pr0: String = json_array_get(project_nodes, 0) let prid0: String = json_get(pr0, "id") let prc0: String = json_get(pr0, "content") let ps0: String = if str_len(prc0) > 120 { str_slice(prc0, 0, 120) } else { prc0 } - if id_in_seen(prid0, seen_ids) || str_eq(ps0, "") { pb } else { "- " + ps0 } - } else { pb } - let pb = if prn > 1 { + if id_in_seen(prid0, seen_ids) || str_eq(ps0, "") { pb_0 } else { "- " + ps0 } + } else { pb_0 } + let pb_2 = if prn > 1 { let pr1: String = json_array_get(project_nodes, 1) let prid1: String = json_get(pr1, "id") let prc1: String = json_get(pr1, "content") let ps1: String = if str_len(prc1) > 120 { str_slice(prc1, 0, 120) } else { prc1 } - if id_in_seen(prid1, seen_ids) || str_eq(ps1, "") { pb } else { pb + "\n- " + ps1 } - } else { pb } - pb + if id_in_seen(prid1, seen_ids) || str_eq(ps1, "") { pb_1 } else { pb_1 + "\n- " + ps1 } + } else { pb_1 } + pb_2 } else { "" } let summary_bullet: String = if summary_ok {