From 88e3008735ad50fcf543947cbd4eb4a64c744e7d Mon Sep 17 00:00:00 2001 From: Neuron Date: Sun, 16 Aug 2026 11:43:38 -0500 Subject: [PATCH] runtime: resume the learned stance in think, instead of discarding it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit engram_think_json built a NEUTRAL stance on every call — cog_stance_init with a NULL id, all axis_gain 1.0, bias_dir NULL, reliability 0.5 — and never loaded the stance the correspondence-beat had been persisting. That mattered because the faculty enters engram_think ONLY through the stance: axis_gain[k] warps the per-axis extents and bias_dir seeds the steering direction. cog_stance_init stores the faculty NAME and nothing reads it. So with a neutral stance, reason/abduce/induce/plan/analogize were byte-identical output under different labels, and confidence was pinned to 0.5 because GeoGradient.confidence IS stance->reliability. The machinery already existed and only this call site ignored it. engram_correspondence_beat_json resumes via cog_stance_from_node and persists via cog_stance_to_node under "stance--". Every beat's calibration was written and then thrown away on the next read. Same defect as the NULL anchor fixed in #142, one line below: a neutral argument collapsing a capability to a constant. Resume the same id the beat writes, so learning compounds across beats and cold boot. Fall back to neutral only when no stance exists — a genuine uninformed prior rather than a discarded informed one. Also emit stance_resumed, so confidence 0.5 from a learned-but-unreliable stance is distinguishable from confidence 0.5 from "no stance exists". That reporting gap is what let the neutral stance hide. Verified against a clone of the production store (13,627 nodes): before beat, no stance stance_resumed=false confidence=0.5 beat on a NON-keystone brier 0.00458568 -> 0.00329654 reduction 28.11%, n_trials 6000, reliability 0.930726, stance_written=true after beat stance_resumed=true confidence=0.930726 Confidence now equals the learned reliability instead of the uninformed prior. The keystone self-anchor correctly stays at 0.5 — calibration is deliberately refused on protected identity regions, and that refusal is now visible as resumed=true with confidence unchanged, rather than being indistinguishable from the bug. STILL OPEN: with no learned bias_dir the faculties remain identical in direction. What distinguishes abduce from induce geometrically is a design decision about how Neuron thinks, not a plumbing defect, and is deliberately left to Will. --- lang/runtime/el_runtime.c | 44 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/lang/runtime/el_runtime.c b/lang/runtime/el_runtime.c index 47917c4..b8eff06 100644 --- a/lang/runtime/el_runtime.c +++ b/lang/runtime/el_runtime.c @@ -13937,7 +13937,40 @@ static int eg_cog_is_keystone_seeds(const char* csv) { el_val_t engram_think_json(el_val_t seeds, el_val_t faculty) { GeoDescriptor* g = eg_geo_build_desc(EL_CSTR(seeds)); if (!g) return eg_geo_err("geometry unavailable"); - CogStance st; cog_stance_init(&st, NULL, EL_CSTR(faculty), g->hub_id, NULL, g); + /* RESUME THE LEARNED STANCE (2026-08-16 self-review). This built a NEUTRAL + * stance every call — all axis_gain 1.0, bias_dir NULL, reliability 0.5 — + * and never loaded the one the correspondence-beat had been persisting. + * + * That mattered because the faculty enters engram_think ONLY through the + * stance: `gain = stance->axis_gain[k]` warps the per-axis extents, and + * `stance->bias_dir` seeds the steering direction. cog_stance_init stores + * the faculty NAME but nothing reads it. So with a neutral stance, + * reason / abduce / induce / plan / analogize are the same function with + * different labels — measured, byte-identical output across all five — + * and `confidence` is pinned to the 0.5 uninformed prior, because + * GeoGradient.confidence is just stance->reliability. + * + * The machinery already existed and only this call site ignored it: + * engram_correspondence_beat_json resumes via cog_stance_from_node and + * persists via cog_stance_to_node under the id "stance--". + * Every beat's calibration was being written and then thrown away on the + * next read. Same defect as the NULL anchor directly above: a neutral + * argument collapsing a capability to a constant. + * + * Resume the same id the beat writes, so learning compounds across beats + * and cold boot. Fall back to neutral only when no stance exists yet — + * which is a genuine uninformed prior, not a discarded informed one. */ + char sid[256]; + snprintf(sid, sizeof sid, "stance-%s-%s", + EL_CSTR(faculty) ? EL_CSTR(faculty) : "reason", + g->hub_id ? g->hub_id : "region"); + CogStance st; StoreNode prev; int resumed = 0; + if (g_engram_store && store_get_node(g_engram_store, sid, &prev) == 1) { + if (cog_stance_from_node(&prev, &st) == 0) resumed = 1; + store_node_free(&prev); + } + if (!resumed) cog_stance_init(&st, sid, EL_CSTR(faculty), g->hub_id, NULL, g); + else { free(st.id); st.id = strdup(sid); } GeoGradient grad; /* ANCHOR THE READ (2026-08-16 self-review). This passed NULL, and NULL is @@ -13999,8 +14032,13 @@ el_val_t engram_think_json(el_val_t seeds, el_val_t faculty) { if (engram_think(g, anchor, &st, &grad) != 0) { free(anchor); cog_stance_free(&st); engram_geo_free(g); return eg_geo_err("think failed"); } free(anchor); JsonBuf b; jb_init(&b); char t[256]; - snprintf(t, sizeof t, "{\"faculty\":\"%s\",\"n_support\":%d,\"magnitude\":%.6g,\"spread\":%.6g,\"confidence\":%.6g,\"dim\":%d", - EL_CSTR(faculty), grad.n_support, grad.magnitude, grad.spread, grad.confidence, grad.dim); + /* stance_resumed distinguishes an INFORMED read from an uninformed one. + * Without it, confidence 0.5 from a learned-but-unreliable stance and + * confidence 0.5 from "no stance exists" are indistinguishable — the same + * reporting gap that let the NULL anchor and the neutral stance hide. */ + snprintf(t, sizeof t, "{\"faculty\":\"%s\",\"n_support\":%d,\"magnitude\":%.6g,\"spread\":%.6g,\"confidence\":%.6g,\"stance_resumed\":%s,\"dim\":%d", + EL_CSTR(faculty), grad.n_support, grad.magnitude, grad.spread, grad.confidence, + resumed ? "true" : "false", grad.dim); jb_puts(&b, t); int emit = grad.dim < 8 ? grad.dim : 8; jb_puts(&b, ",\"direction\":"); eg_geo_emit_vec(&b, grad.direction, emit);