Compare commits

..

2 Commits

Author SHA1 Message Date
Neuron 88e3008735 runtime: resume the learned stance in think, instead of discarding it
El SDK CI - dev / build-and-test (pull_request) Failing after 4m16s
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-<faculty>-<hub>". 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.
2026-08-16 11:43:38 -05:00
will.anderson a6cef4b983 runtime: publish the vector index instead of guarding it (#143)
El SDK CI - dev / build-and-test (push) Failing after 10m29s
2026-08-16 16:33:28 +00:00
+41 -3
View File
@@ -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-<faculty>-<hub>".
* 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);