# Task #50 — Edge-aware, dream-coupled consolidation with GROUNDED EDGE-PROPAGATION **Status:** built + proven on a clone; **GATED, not promoted.** The main loop sequences live promotion after the engine/HNSW cutover settles. **Date:** 2026-08-15 · **Worktree:** `agent-a6577c8211c332c5b` (isolated). Grounding mechanism designed with Will (memory `9e09a59f`, refining `1a861007`). This is the HOW for #50. --- ## (a) How grounded edge-propagation integrates into the dream/consolidation cycle The beat already exists. `neuron/awareness.el` runs a heartbeat (~every `beat_ms`); each beat calls `hebb_consolidate()` — which drains the self-formed Hebbian associations out of the fast in-process store and writes them, over the threshold `ENGRAM_HEBB_LINK_MIN`, into the durable engram (`:8742`) — and then `emit_heartbeat()`. Grounded edge-propagation slots into the **same beat, immediately after consolidation** (awareness.el line 1286–1288): ``` hebb_consolidate() // lay down the tethers (edges) that cleared threshold ground_propagate() // <-- NEW: grade beliefs ALONG those tethers emit_heartbeat() // report gep_* gauges beside hebb_* ``` This ordering is the point. Consolidation lays down the wiring; propagation grades the beliefs along it, in the same breath. Memory `69b8babe`: memory-consolidation and staying-yourself are one physics — forming a memory and grading a belief are the same gravity run in two passes of one beat. The propagation runs **inside the engram** as the native `engram_ground_propagate()` over the durable flat node/edge arrays (the store the consolidated edges just landed in). The soul invokes it over HTTP (`POST /api/ground/propagate`) and folds the returned `gep_*` telemetry into the heartbeat stream next to `hebb_cands / hebb_mass / hebb_edges`. **Bounded by construction** (per the live-graph reality — 70.7% of nodes isolated, connected core ~28%, hub first-hop fan-out in the thousands): - **1-hop only.** No BFS spreading activation — a belief is graded from its DIRECT grounded neighbors, so there is no per-hop breadth explosion. - **Beam-capped** at `GEP_MAX_CORR = 256` corroborators per belief. - **Salience-ordered, `GEP_BELIEFS_PER_BEAT = 512`** beliefs per beat; the rest next beat. Work per beat is O(beliefs × degree), hard-bounded. - **Isolated / starved beliefs** are counted and surfaced (`gep_isolated`, `gep_starved`) as an interoceptive sparse-region signal for the edge-formation / embedding pass (#20). #50 CONSUMES edges; it does not form them. A belief with no grounded neighbor has nothing to tether to — correct per the anti-delusion gravity law (`0b15017c`), not a gap. --- ## (b) The implementation Represented faithfully to the spec — **grounding is a Hebbian-weighted collection over time, never a scalar.** - **Grounding = an append-only event ring** on the node (`GepGrounding`), structurally parallel to the ACT-R base-level access ring already in `EngramNode` (`access_ts[K]`). Each event is `{ts, sign±, mag, corroborator signature}`. Append-only, supersede-not-delete; events aged out of the ring are counted (`older_count`), never faked away. - **Standing is DERIVED, recency-weighted, never stored** — `standing = clamp(GEP_BASE + Σ_events sign·mag·age^(-D), 0, 1)`, exactly the ACT-R base-level shape `ln Σ t^-d` (`ENGRAM_BLL_D = 0.5`) but sign-carrying so LTD subtracts. Memory `1a861007`: the collection is primary, the standing is its emergent aggregate. Mirrored onto `confidence` each beat so downstream reads (verifier #43, realizer calibration `0041d917`) never speak above the grounding. - **Update = LTP/LTD with a threshold.** Per belief, gather corroborators along incident edges, weighted by `edge.weight` (the Hebbian weight) × the neighbor's own standing. **Anti-delusion gravity:** only neighbors already `≥ GEP_LIKELY_MIN` may corroborate — grounding flows FROM the grounded core. - **Convergent INDEPENDENT corroboration** is the driver. Independence is enforced by **union-find over the corroborator set**: two corroborators are the same independent source if they are the same node, reached by multiple edges, or linked to each other (an echo chain / shared derivation). Support is summed **per independent component** (max-magnitude member), and the threshold gate requires BOTH a mass floor (`pos ≥ GEP_THETA`) AND an independence-count floor (`n_independent ≥ GEP_N_MIN`). The count gate is the guard against one node echoed N times. - **Sub-threshold is transient.** Support present but below threshold → `subthreshold_hits++`, no durable event, no lasting shift (Will's exact spec). - **Graduation / decay.** Cross up → LTP event appended → standing climbs `conjecture → likely → grounded`. Contradiction past threshold → LTD → `grounded → likely → conjecture`. Nothing latches; withdraw support and the collection ages and relaxes (`271f1163`, nothing is settled). ### Files | File | Role | |---|---| | `gep_core.h` | The mechanism. Pure C, libm only (own-the-core). Single source of truth: `GepGrounding`, `gep_standing`, `gep_append`, union-find independence, `gep_propagate_node`, `gep_beat`. | | `gep_proof.c` | Self-contained proof harness — builds the three scenarios, prints raw before/after. | | `engram_ground_propagate.staged.c` | GATED runtime native. Wires the SAME `gep_core.h` primitives to the live `EngramStore` (adj cache, flat arrays). Splice plan + relation→polarity + belief gate. Compiles only when spliced (verified: every runtime symbol it references — `engram_adj_rebuild`, `adj_from_len`, `engram_find_node_index`, `ENGRAM_LAYER_SAFETY`, `istr_contains`, … — exists in the release runtime). | | `awareness.beat.patch.el` | GATED beat hook — `ground_propagate()` + the insert between `hebb_consolidate()` and `emit_heartbeat()`. | | `server.route.patch.el` | GATED route — `POST /api/ground/propagate`. | ### Constants `BASE=0.10 LIKELY_MIN=0.34 GROUNDED_MIN=0.66 N_MIN=3 THETA=0.30 D=0.5` (`N_MIN` parameterizes Will's "13 adjacent things" — the count threshold is a knob; 3 here for a crisp proof.) --- ## (c) PROOF LEDGER — raw grounding before/after Deterministic. Build `cc -std=c11 -O2 -o gep_proof gep_proof.c -lm`, run `./gep_proof` (full transcript in `PROOF_OUTPUT.txt`). ### (a) STRENGTHEN — convergent independent corroboration graduates a conjecture | beat | event | pos_mass (n_indep) | action | standing before → after | band | |---|---|---|---|---|---| | 1 | 3 independent grounded corroborators | 0.4050 (3) | **LTP** | 0.1000 → **0.4842** | conjecture → **likely** ⬆ | | 2 | neighborhood grows to 5 | 0.6750 (5) | **LTP** | 0.1496 → **0.7379** | conjecture → **grounded** ⬆ | | 3 | support sustained (5) | 0.6750 (5) | LTP | 0.2110 → 0.7993 | grounded (sustained) | | 4 | corroboration withdrawn (+10min) | 0.0000 (0) | isolated | 0.1612 → 0.1612 | relaxing | | 5 | still withdrawn (+1h) | — | isolated | 0.1263 | relaxing | | 6 | still withdrawn (+4h) | — | isolated | 0.1130 | → conjecture | Grounding grew **on its own** past threshold and graduated conjecture → likely → grounded, then **relaxed** once independent support stopped. Living, not a latched flag. ### (b) DECAY — convergent independent contradiction erodes a grounded belief | beat | event | neg_mass (n_indep) | action | standing before → after | band | |---|---|---|---|---|---| | — | seed (prior LTP) | — | — | **0.9500** | grounded | | 1 | 3 independent contradictions | 0.5400 (3) | **LTD** | 0.9500 → **0.4570** | grounded → **likely** ⬇ | | 2 | contradiction broadens to 5 | 0.9000 (5) | **LTD** | 0.1461 → **0.0000** | conjecture ⬇ | | 3–4 | contradiction sustained (5) | 0.9000 (5) | LTD | 0.0000 | conjecture | Grounding decayed grounded → likely → conjecture under accreting independent contradiction. The door never shut — history is retained (the event ring keeps growing), the belief stays falsifiable in both directions. ### (c) INDEPENDENCE GUARD — the load-bearing property Identical fan-in (N=5), identical edge weight (0.30), identical corroborator standing (~0.90). **The only difference is whether the five are independent.** | sub-case | topology | pos_mass | **n_indep** | action | standing 0.1000 → | |---|---|---|---|---|---| | **C1** | 5 DISTINCT, no inter-links | 1.3500 | **5** | **LTP** | **0.9741 (grounded)** ⬆ | | **C2** | 5 mutually-linked (echo of one source) | 0.2700 | **1** | sub-threshold | 0.1000 (unchanged) | | **C3** | 1 node reached by 5 parallel edges | 0.2700 | **1** | sub-threshold | 0.1000 (unchanged) | Same raw fan-in, opposite outcome. Union-find collapses the echoes to a single independent component; the count gate (`n_indep ≥ N_MIN`) then refuses them. **Circular self-reinforcement cannot manufacture grounding** — a conjecture can only be grounded by evidence that is genuinely independent of itself. --- **RAILS honored:** isolated worktree; built/proven on a clone; the live soul (`:8742` / `:7770`) untouched; no fight with the cutover (built against current release source; staged native rebases cleanly onto it); no new libraries (libm only); identity keystones untouched. **Not promoted** — gated artifact + ledger for the main loop to sequence.