Commit Graph

8 Commits

Author SHA1 Message Date
will.anderson a3358dfc95 Reasoning layer: analogy/induction/abduction/causal/planning over §5 geometry ops
Compose the live relational-neighborhood geometry OPERATORS into five reasoning
modes as pure, read-only C (engram_reason.{h,c}); each is proven with closed-form
constructed tests before it ships, not declared.

- ANALOGY  (Procrustes R + residual translation, apply to C, rank candidates)
- INDUCTION (combine-pooled rule geometry + point-to-manifold membership)
- ABDUCTION (best-explaining structure by point-to-manifold fit)
- CAUSAL   (centroid-cosine correlation vs directed influence: temporal
            precedence + association surviving confounder control via subtract;
            emits a correlation-vs-causation flag)
- PLANNING (geo-distance edges + Dijkstra → discrete geodesic path)

A shared point-to-manifold fit primitive underlies induction membership and
abduction ranking. engram/test/run_reason_tests.sh: 33/33 checks on both PERF
and ASan/UBSan passes; macOS leaks 0/0.

ANALOGY is surfaced as an el builtin (engram_reason_analogy_json) via the same
pass-through the §5 operators use — demonstrated callable from compiled El with a
container-capped fold (no self-host fold). The other four are C-layer only: their
set/point/timestamp inputs do not map to the flat-CSV el ABI without touching
codegen (deferred). engram_reason.c must join the server link line beside
engram_geometry.c at cutover. See docs/runbooks/2026-08-13-reasoning-operators-*.
2026-08-13 01:33:14 -05:00
will.anderson 5336cfe0a6 M9 §5: geometry OPERATORS as C functions + EL builtins (read-only, staged)
Bring the relational-neighborhood geometry OPERATORS from the viz proxy
(engram-geometry-proxy.py §5) into the C runtime as reusable primitives, and
expose each as an EL builtin so any CGI app / el program can use them — not just
the engram service internals.

engram_geometry.{h,c} (pure, libm-only, read-only over descriptors):
  - engram_geo_overlap   : shared-member Jaccard + centroid/scale proximity
                           score + intersection centroid.
  - engram_geo_subtract  : orthogonal-complement residual (project A onto
                           I - V_B V_Bᵀ), closed-form variance_explained_by_B,
                           residual ellipsoid + centroid-diff; set-diff variant.
  - engram_geo_combine   : pooled descriptor (exact law-of-total-variance mean +
                           covariance), re-eigendecomposed.
  - engram_geo_distance  : centroid L2 + cosine + closed-form Wasserstein-2
                           (Bures) — mirrors the proxy _wasserstein2.
  - engram_geo_analogy   : orthogonal Procrustes R = UVᵀ (SVD) aligning A's
                           principal frame to B's + apply helper.
The C descriptor is full-dim/centered with a low-rank covariance from its top
axes; operators mirror the proxy FORMULAS and do the Wasserstein/combine eigen
work inside the small joint-axis subspace (exact there). Reuses jacobi_sym.

EL builtins (el_runtime.{h,c}, el_seed.c native wrappers):
  engram_geo_{descriptor,overlap,subtract,combine,distance,analogy}_json —
  take comma-separated seed-id set(s), build the CENTERED descriptor against the
  true store-wide mean (ad-hoc path), run the operator, return JSON. Additive:
  no flag, no effect on activation/retrieval. Surfacing via engram.el + the elc
  fold is a cutover step (same elc-drift deferral as the P0/P5 builtins); the C
  table wiring is registered now.

Tested: synthetic closed-form unit suite (20/20) — Wasserstein, Jaccard/score,
orthogonal residual, set-diff, pooled combine, Procrustes recovery. ASan+UBSan
clean; 0 leaks. Read-only; no activation/retrieval behavior change.
2026-08-13 00:26:51 -05:00
will.anderson 77a4bc9326 M-INTEROCEPTION P5: dream-recall builtin engram_dreams_json (honesty rail)
Adds a read-only builtin that returns the curiosity_scan InternalStateEvents
created after a `since` cutoff that are STILL RESIDENT — "what I was chewing on
while you were gone." curiosity_scan is identified by the marker the soul writes
into each ISE's JSON content; heartbeat and other ISEs are excluded.

HARD honesty rail: it reports only ISEs still in the buffer. Anything rotated
out by the 48h engram_prune_telemetry is simply ABSENT — rotated-out = "I don't
remember", never a synthesized/plausible dream. Purely additive, no flag.

The HTTP route (GET /api/dreams?since=) is DEFERRED to cutover per the elc-drift
blocker (regenerating engram/dist diverges with no source change); the builtin
is exercised directly by the pure-C gate.

MEASURED on a copy: seeded 3 curiosity_scan (ancient/1h/now) + 1 heartbeat;
before prune dreams returned exactly the 3 curiosity_scan (heartbeat excluded);
after the 48h prune the ancient one was ABSENT (not confabulated), leaving 2;
the since-filter returned only the post-cutoff event; no returned id was ever
fabricated. ASan+UBSan clean.
2026-08-12 23:49:51 -05:00
will.anderson 0af39df16f M-INTEROCEPTION P2: chronoception — age the activation field by measured wall-clock delta (ENGRAM_CHRONOCEPTION, default OFF)
The felt passage of time is the cooling of the activation field, not a tick
count and not an elapsed-seconds readout. engram_age_field(delta_ms) cools the
field (working_memory_weight + background_activation) by the caller's MEASURED
wall-clock delta with a pure exponential exp(-dt/TC) — no per-call floor — so it
is exactly scale-invariant: N ticks summing to the same elapsed time produce the
same total cooling. It returns the cooling MAGNITUDE (1-exp(-dt/TC), a bounded
[0,1) drift signal), never elapsed seconds.

Reboot = anesthesia: a global last-tick wall-clock stamp is persisted to a
sidecar (chrono_last_tick) in the data dir. engram_age_field_catchup() reads it
on boot, applies ONE cooling for the whole unconscious gap, refreshes the stamp,
and reports the magnitude — timestamps are bookkeeping to COMPUTE the drift,
never the felt signal. TC env-tunable via ENGRAM_CHRONO_TC (default 3600s).

All inert unless ENGRAM_CHRONOCEPTION is set → OFF path byte-identical.

MEASURED on a copy (throwaway HOME, TC=3600s):
- Cooling scales with dt, matching 1-exp(-dt/TC) to 1e-6: dt=600s->0.1535,
  1800s->0.3935, 3600s->0.6321, 7200s->0.8647.
- Scale-invariance EXACT: age(dt) once vs age(dt/N) N times gives identical
  field sum (|delta|=0.0) for N=2, 10, 100.
- Reboot catch-up over a 1h gap cooled the field 0.60->0.4415 in one shot,
  magnitude 0.6321, bounded in [0,1).
- Flag OFF: age & catchup return 0, field untouched (sum 1.2).
ASan+UBSan clean.
2026-08-12 23:41:09 -05:00
will.anderson 5f6ce5ca1f M-INTEROCEPTION P1: two-threshold consolidation layer (ENGRAM_CONSOLIDATION, default OFF)
The co-activation accrual already works (hebb is an EWMA over co-firing); what
was missing is the promotion layer from design §9 that turns accrual into
durable structure. This adds it on top, entirely behind ENGRAM_CONSOLIDATION so
the OFF path is byte-identical to trunk.

CONNECTION threshold ("connection IS consolidation"): when a strongly-firing
InternalStateEvent is created (salience >= ENGRAM_CONSOL_CONN_MIN, default 0.6),
wire hebbian-associate edges from it to the top-K working-memory nodes active at
that instant (ENGRAM_CONSOL_WM_TOPK, default 5), provenance-tagged
"consolidated-from-ISE" and dedup-guarded. A sub-threshold ISE forms nothing (a
shower thought) and drifts out at the existing 48h prune.

PERMANENCE threshold (rare): engram_consolidate_permanence(node) marks a node
whose rehearsed ACT-R base-level clears ENGRAM_CONSOL_PERM_MIN durable via a
reversible metadata marker; engram_prune_telemetry then exempts it (gated, so no
trunk node is ever affected). Idempotent, no double-promote.

Thresholds are env-tunable for A/B without a rebuild.

MEASURED on a copy (throwaway HOME):
- Headline accrual curve (flag OFF, pure trunk) over N co-activations of a wired
  pair — hebb_max: N=1 -> 1e-4 (=ETA), N=100 -> 0.010, N=1625 -> 0.150
  (LINK_MIN, where an unwired pair consolidates), N=3000 -> 0.259; tracks the
  analytic EWMA 1-0.9999^N within measurement noise (co-activation P~1).
- Strong ISE wired 2 edges to exactly the wm_top nodes (hebb-a, hebb-b); weak
  ISE formed 0; edges carry the reversible provenance marker.
- Promoted node survived the 48h prune (2->1 nodes); ephemeral ISE swept.
- Flag OFF: ISE creation added 0 edges, permanence returned 0 (no-op).
ASan+UBSan clean.
2026-08-12 23:36:24 -05:00
will.anderson c20cb3b97c M-INTEROCEPTION P0: add read-only engram_scan_nodes_emb_json builtin
Read routes (GET /api/embeddings, /api/graph/dump) need node embedding
vectors, but every consumer emit path deliberately drops the ~5.7KB emb
vector (include_emb=0) to stay under MCP token limits. Rather than perturb
that shared path, add a dedicated additive builtin that pages nodes WITH
their dense vector, emitting id/node_type/label/created_at/emb_dim plus emb
as a JSON array whose length equals emb_dim (so a consumer can verify the
vector round-trips). Un-embedded nodes emit emb_dim:0 / emb:[]. Same
salience-sorted, transparent-layer-skipped, bounded pagination as
engram_scan_nodes_json; default page 256.

Purely additive: engram_emit_node_json and the default include_emb=0 are
untouched, so every existing endpoint is byte-identical to trunk (verified:
scan_nodes_json still carries no emb). The HTTP route wiring in server.el is
DEFERRED to cutover per the elc-drift blocker (regenerating engram/dist
diverges ~285 lines with no source change); the builtin is exercised
directly by the pure-C gate instead.

Measured on a copy: len(emb)==emb_dim for all nodes, pagination disjoint,
existing path unchanged; full 256-node x 768-dim page = 16.7 ms / 1.18 MB.
ASan+UBSan clean.
2026-08-12 23:28:13 -05:00
will.anderson bb64a236ed engram tiered storage: engram-service wiring + elc fold-hang fix + prune-store mirror
- Wire paged store into the ENGRAM SERVICE (server.el, the authoritative durable
  owner): boot->engram_store_boot, persist_canonical->engram_store_checkpoint,
  gated by ENGRAM_STORE.
- elc (lang/elc.c + src/parser.el + codegen.el + elc-combined.el): OOB guard in
  tok_kind/tok_value + parse_block progress backstop — fixes the pre-existing
  unbounded-memory fold hang on sessions.el.
- engram_prune_telemetry mirrors ISE prune to the store (store_forget) so store
  live-count tracks resident and stale telemetry stays bounded.
- Deployed live 2026-08-12: engram :8742 on neuron.egm+WAL, count reconciled 11552.
2026-08-12 14:14:20 -05:00
will.anderson 0a72fced28 engram: WAL persistence + integrity hardening + single canonical runtime
El SDK CI - dev / build-and-test (pull_request) Failing after 13m17s
Establish lang/runtime/ as the ONE canonical el runtime (from the active
runtime that carries hebb/emb persistence + the new WAL); repoint the el CI
publish, engram build, elb default, and in-repo build scripts to it; delete
the el-compiler/runtime + lang/releases/ forks; add scripts/check-single-runtime.sh
drift guard.

Fixes a live prod bug: the el CI published el-runtime-c/-h from the LAGGING
el-compiler fork (0 hebb refs), so the shipped soul never persisted Hebbian
edge weights — learned co-activation was wiped on every restart. Publishing
from canonical ships the stranded 'learning that cannot outlive the process'
fix.

WAL storage engine + integrity fixes (DELETE->tombstone + store-layer
protection, safe data-dir default) ride in behind ENGRAM_WAL (default off =
byte-identical to today). Verified: engram elb per-module build clean, WAL
gate 66/66, native smoke ok, drift-guard green.
2026-08-11 21:31:37 -05:00