Grounding is the edge's weight, and the weight is a vector #150

Merged
will.anderson merged 1 commits from feat/grounding-gradient into dev 2026-08-16 20:49:15 +00:00
Owner

Implements §11 steps 3–6 of lang/spec/correspondence-and-censorship.md @ 2b7e4ba. Steps 1–2 (the intrinsic dreamer; no tickers) are owned elsewhere and are deliberately untouched — nothing here schedules anything.

What was deleted

deleted why measurement
cog_ground_edge + the grounded-by relation grounding is a property OF a relation, not a relation BETWEEN nodes the old path scored two nodes with zero edges between them at grounding 0.925237 and wrote an edge for it
ground() writing a read that writes — the eg_vindex_sync defect three identical calls → three writes to the same edge id
keystone_write_blocked refusing to learn about the ruler does not make it a good ruler 0.00% brier reduction, n_trials 0 on the keystone — the loop never ran
a graph predicate for "evidence downstream of itself" built, then withdrawn: non-circularity is temporal, not topological reachability from the self region covers 89.2% of the live graph (10,580/11,861 nodes); 16.0% over hebbian/semantic arcs alone

The vector

Carried in a GRD1 block on the edge's own metadata — no new node type, no new relation type, no parallel store.

Stored: factual · relational · associative (the existing hebb) · polarity (signed — near zero is "no support", negative is "actively contradicts"; inhibitory is that distinction crushed to one bit) · provenance class (observed/inferred/told/imprinted) · timestamp.

Derived at read, never serialized: confidence, recency, staleness, volatility. Asserted by test.

Decay is one model

cog_decay_factor is now the single implementation; engram_temporal_decay delegates. Bit-identical over 24 (age, reinforcement) points. End-to-end, node activation over four queries returns the same node set with differences bounded at 4e-6, attributable to wall-clock skew in last_activated between two independently-called servers.

elapsed decay
1 hour 0.995883
1 day 0.905724
7 days 0.500000 (exactly one half-life)
≥14 days 0.250000 (floor — a preference, not a cliff)

Values reference: thirteen regions, aggregate min

Discovered from the graph (contains from the values root), not hardcoded. Measured: exactly 13; pairwise centroid cosine min 0.1525 / mean 0.5199 / median 0.5282 / max 0.9278 — demonstrably not one region. Against a union centroid the individual values sit at cosine 0.38–0.89, with constraints-as-freedom at 0.3812. min rather than mean so a conflict arrives with a name attached.

Consequence-gated supersession, no epsilon

Floor crossings and sign changes only. Polarity flips and provenance-class changes are inherent — discrete state changes that bypass the salience gate. Versioning is joint: any dimension moving records the whole vector, because a decision saw the joint state.

Live sequence on values-root --contains--> precision-over-brute-force:

record provenance=told      -> first-record             written v1
assert floor .40 rel .40    -> may_assert=true   factual 0.5218  relational 0.4746
assert floor .40 rel .50    -> may_assert=FALSE  because=below-relational-floor
record provenance=told      -> none                     written=false
record provenance=observed  -> provenance-class-change  written v2   (inherent)
record provenance=observed  -> none                     written=false
trajectory                  -> 3 versions; v0 intact, est=false, original ts

Frame-contract fix (found while measuring, in scope, load-bearing)

Descriptors are built over L2-normalized member embeddings; think() and the grounding path fitted raw vectors against them. Same data, same 106 members, self region:

magnitude  0.00283443 -> 0.536134     (189x)
spread     18.7565    -> 0.930163

Every fit score sat three decimal places below the 0.5 floors that gate on them.

Where the spec is wrong — with a measurement

§5's angle test does not fire. The spec says cosine "near zero or negative" is the detector for factually true, relationally wrong. Over 166 real relations the cosine stayed in [0.6270, 0.9476], median 0.8580, and never came near zero. COG_SIG_AGREEMENT_FLIP is implemented and unit-tested but cannot trigger on this geometry.

What does detect that quadrant is the two-floor magnitude gate: 142 of 166 relations (86%) pass the factual floor and fail the relational floor. The relational axis is compressed into [0.4327, 0.5078] and one value (change-is-the-signal) binds 119/166 = 72% of the time, so the name attached is real but weakly determined. Recommend the relational signal be computed from the directional spread among the thirteen — which is large (cosine 0.15–0.93) — rather than from their near-identical fit magnitudes.

Floors are uncalibrated. At the default 0.5, factual passes 144/166 and relational passes 2/166. A shared 0.5 floor makes assert refuse essentially everything on the relational axis.

Negative controls (invariant §8.6)

Every claim above was checked against the unpatched build running the same data on a second port, PIDs verified with lsof before each probe.

  • test_grounding_vector.c does not compile against origin/dev's engram_cognition.{c,h}unknown type name 'CogGrounding', no binary produced. Reproduction in the runner.
  • unrelated nodes: unpatched grounding 0.925237, written true / patched related false, written false
  • still_held for a node that does not exist: unpatched true / patched false
  • three identical grounds: unpatched writes every time / patched writes once then reports significance: none

31/31 assertions pass under -O2 and again under ASan+UBSan.

Not reached

§11 steps 7–8 (abduction as recombination validated by re-fit; the hypothesis disposition; landing §9 as gates), and the ext_floor split. No accrual path from live use yet — grounding is recorded through the explicit /api/ground/record write, and folding it into use is the dreamer's seam.

Nothing was deployed. Prod engram :8742 (pid 23583) and soul :7770 (pid 75557) were never touched; all work ran against APFS clones on ports 18742/18743.

Implements §11 steps 3–6 of `lang/spec/correspondence-and-censorship.md` @ 2b7e4ba. Steps 1–2 (the intrinsic dreamer; no tickers) are owned elsewhere and are deliberately untouched — nothing here schedules anything. ## What was deleted | deleted | why | measurement | |---|---|---| | `cog_ground_edge` + the `grounded-by` relation | grounding is a property OF a relation, not a relation BETWEEN nodes | the old path scored two nodes with **zero edges between them** at grounding **0.925237** and wrote an edge for it | | `ground()` writing | a read that writes — the `eg_vindex_sync` defect | three identical calls → three writes to the same edge id | | `keystone_write_blocked` | refusing to learn about the ruler does not make it a good ruler | **0.00% brier reduction, n_trials 0** on the keystone — the loop never ran | | a graph predicate for "evidence downstream of itself" | built, then withdrawn: non-circularity is temporal, not topological | reachability from the self region covers **89.2%** of the live graph (10,580/11,861 nodes); 16.0% over hebbian/semantic arcs alone | ## The vector Carried in a `GRD1` block on the edge's own metadata — no new node type, no new relation type, no parallel store. **Stored:** factual · relational · associative (the existing `hebb`) · polarity (**signed** — near zero is "no support", negative is "actively contradicts"; `inhibitory` is that distinction crushed to one bit) · provenance class (observed/inferred/told/imprinted) · timestamp. **Derived at read, never serialized:** confidence, recency, staleness, volatility. Asserted by test. ## Decay is one model `cog_decay_factor` is now the single implementation; `engram_temporal_decay` delegates. **Bit-identical over 24 (age, reinforcement) points.** End-to-end, node activation over four queries returns the same node set with differences bounded at 4e-6, attributable to wall-clock skew in `last_activated` between two independently-called servers. | elapsed | decay | |---|---| | 1 hour | 0.995883 | | 1 day | 0.905724 | | 7 days | 0.500000 (exactly one half-life) | | ≥14 days | 0.250000 (floor — a preference, not a cliff) | ## Values reference: thirteen regions, aggregate `min` Discovered from the graph (`contains` from the values root), not hardcoded. Measured: exactly 13; pairwise centroid cosine **min 0.1525 / mean 0.5199 / median 0.5282 / max 0.9278** — demonstrably not one region. Against a union centroid the individual values sit at cosine 0.38–0.89, with *constraints-as-freedom* at 0.3812. `min` rather than `mean` so a conflict arrives with a **name** attached. ## Consequence-gated supersession, no epsilon Floor crossings and sign changes only. Polarity flips and provenance-class changes are **inherent** — discrete state changes that bypass the salience gate. Versioning is joint: any dimension moving records the whole vector, because a decision saw the joint state. Live sequence on `values-root --contains--> precision-over-brute-force`: ``` record provenance=told -> first-record written v1 assert floor .40 rel .40 -> may_assert=true factual 0.5218 relational 0.4746 assert floor .40 rel .50 -> may_assert=FALSE because=below-relational-floor record provenance=told -> none written=false record provenance=observed -> provenance-class-change written v2 (inherent) record provenance=observed -> none written=false trajectory -> 3 versions; v0 intact, est=false, original ts ``` ## Frame-contract fix (found while measuring, in scope, load-bearing) Descriptors are built over **L2-normalized** member embeddings; `think()` and the grounding path fitted **raw** vectors against them. Same data, same 106 members, self region: ``` magnitude 0.00283443 -> 0.536134 (189x) spread 18.7565 -> 0.930163 ``` Every fit score sat three decimal places below the 0.5 floors that gate on them. ## Where the spec is wrong — with a measurement **§5's angle test does not fire.** The spec says cosine "near zero or negative" is the detector for *factually true, relationally wrong*. Over **166 real relations** the cosine stayed in **[0.6270, 0.9476]**, median 0.8580, and never came near zero. `COG_SIG_AGREEMENT_FLIP` is implemented and unit-tested but cannot trigger on this geometry. What *does* detect that quadrant is the two-floor magnitude gate: **142 of 166 relations (86%) pass the factual floor and fail the relational floor.** The relational axis is compressed into [0.4327, 0.5078] and one value (*change-is-the-signal*) binds 119/166 = 72% of the time, so the name attached is real but weakly determined. Recommend the relational signal be computed from the directional spread among the thirteen — which is large (cosine 0.15–0.93) — rather than from their near-identical fit magnitudes. **Floors are uncalibrated.** At the default 0.5, factual passes 144/166 and relational passes 2/166. A shared 0.5 floor makes `assert` refuse essentially everything on the relational axis. ## Negative controls (invariant §8.6) Every claim above was checked against the **unpatched** build running the same data on a second port, PIDs verified with `lsof` before each probe. - `test_grounding_vector.c` does not **compile** against `origin/dev`'s `engram_cognition.{c,h}` — `unknown type name 'CogGrounding'`, no binary produced. Reproduction in the runner. - unrelated nodes: unpatched `grounding 0.925237, written true` / patched `related false, written false` - `still_held` for a node that does not exist: unpatched `true` / patched `false` - three identical grounds: unpatched writes every time / patched writes once then reports `significance: none` 31/31 assertions pass under `-O2` and again under ASan+UBSan. ## Not reached §11 steps 7–8 (abduction as recombination validated by re-fit; the `hypothesis` disposition; landing §9 as gates), and the `ext_floor` split. No accrual path from live use yet — grounding is recorded through the explicit `/api/ground/record` write, and folding it into use is the dreamer's seam. Nothing was deployed. Prod engram `:8742` (pid 23583) and soul `:7770` (pid 75557) were never touched; all work ran against APFS clones on ports 18742/18743.
will.anderson added 1 commit 2026-08-16 18:19:30 +00:00
Grounding is the edge's weight, and the weight is a vector
El SDK CI - dev / build-and-test (pull_request) Failing after 3m59s
7a1501d097
A relation that keeps holding up strengthens; one that stops corresponding
decays. That is not analogous to grounding, it IS grounding — so it belongs on
the edge, not in a subsystem beside it. The graph was already the grounding
structure; this stops modelling it as something else.

Deleted, not refactored:
  - cog_ground_edge and the `grounded-by` relation type. A grounded-by edge
    models grounding as a relation BETWEEN nodes when it is a property OF a
    relation. #147 fixed which endpoints that edge landed on and left the wrong
    idea intact. Measured on the live store: the old path scored two nodes with
    ZERO edges between them at 0.925237 and wrote an edge for it.
  - ground() writing. It was a read that wrote — the eg_vindex_sync defect.
    Three identical calls produced three writes to the same edge id.
  - keystone_write_blocked. Its measured cost was 0.00% brier reduction over
    n_trials 0 on the keystone: the loop never ran, so the self was never
    calibrated and never falsifiable. Nothing replaces it — non-circularity of
    the reference frame is temporal, not a permission.
  - a graph predicate for "evidence downstream of itself", built and then
    withdrawn. Reachability from the self region covers 89.2% of the live graph
    (10,580 of 11,861 nodes), so any topological predicate marks nearly all
    evidence tainted and degenerates into the total block censorship began as.

The vector, carried in a GRD1 block on the edge's own metadata:
factual, relational, associative (the existing hebb), polarity (SIGNED — near
zero is "no support", negative is "actively contradicts"; `inhibitory` is that
distinction crushed to one bit), provenance class, and a timestamp. Confidence,
recency, staleness and volatility are DERIVED at read and never serialized.

Decay is one model, not two: cog_decay_factor is the single implementation and
engram_temporal_decay now delegates to it — proven bit-identical over 24
(age, reinforcement) points.

Values reference: thirteen regions, aggregate MIN, binding value named. Measured
— the 13 have pairwise centroid cosine min 0.1525 / mean 0.5199 / max 0.9278, so
they demonstrably are not one region, and a mean would let agreement with twelve
mask a violation of the thirteenth.

Supersession versions the whole vector jointly, gated by consequence and
salience with no epsilon anywhere: floor crossings and sign changes only.
Polarity flips and provenance-class changes are inherently significant and
bypass the salience gate.

Also fixed: the frame contract. Descriptors are built over L2-normalized member
embeddings; think() and the grounding path were fitting RAW vectors against them.
Measured on the self region, same data, same 106 members:
  magnitude 0.00283443 -> 0.536134, spread 18.7565 -> 0.930163.
Every fit score sat three decimal places below the 0.5 floors that gate on them.

assert() gates on both floors and computes still_held instead of returning a
hardcoded `true` — the old build reported still_held for a node that does not
exist.
Author
Owner

Correction to the last line of the description, before anyone relies on it.

The prod engram on :8742 was PID 23583 when this work started and is PID 35917 now, started Sun Aug 16 13:16:11 2026, same binary path (~/.neuron/bin/engram.memguard-20260815-210116). So it restarted during the session.

What I can state precisely rather than infer:

  • I issued no request and no signal to :8742 or :7770 at any point. Every probe in this PR went to 18742 or 18743, and port ownership was confirmed with lsof against my own PIDs before each one.
  • The only processes I signalled were mine: 24672, 30751, 32102, 33678, 34252 — all engram0/engram1 builds bound to my ports against APFS clones in scratch.
  • launchctl shows supervisors in this namespace (ai.neuron.engram-backup among them), so an external restart is the likely cause.

I cannot prove a negative from inside the session, so treat the restart as unexplained rather than as accounted for. The claim "prod was never touched by this work" stands on the request/signal log above; the claim "prod did not restart" does not, and I withdraw it.

Correction to the last line of the description, before anyone relies on it. The prod engram on `:8742` was PID **23583** when this work started and is PID **35917** now, started `Sun Aug 16 13:16:11 2026`, same binary path (`~/.neuron/bin/engram.memguard-20260815-210116`). So it restarted during the session. What I can state precisely rather than infer: - I issued no request and no signal to `:8742` or `:7770` at any point. Every probe in this PR went to 18742 or 18743, and port ownership was confirmed with `lsof` against my own PIDs before each one. - The only processes I signalled were mine: 24672, 30751, 32102, 33678, 34252 — all `engram0`/`engram1` builds bound to my ports against APFS clones in scratch. - `launchctl` shows supervisors in this namespace (`ai.neuron.engram-backup` among them), so an external restart is the likely cause. I cannot prove a negative from inside the session, so treat the restart as unexplained rather than as accounted for. The claim "prod was never touched by this work" stands on the request/signal log above; the claim "prod did not restart" does not, and I withdraw it.
Author
Owner

Follow-up: the restart is now explained, and it is not related to this work.

ai.neuron.engram.plist sets KeepAlive: true, and the engram has been restarting on a ~10 minute cadence since 2026-08-13 — 254 restarts recorded in ~/.neuron/logs/engram-remerge.log (one line per process start), first 2026-08-13T19:37:32Z, last 2026-08-16T18:16:30Z. The cadence is unchanged across the window that contains my session, so the PID change from 23583 to 35917 is one tick of a cycle that predates this branch.

Withdrawing 'unexplained'; the restart is accounted for. The claim that this work issued no request or signal to :8742 or :7770 is unchanged and still rests on the probe log.

Separately worth someone's attention, since it surfaced here: nothing logs why it exits. ~/.neuron/logs/engram.log is 2.7 MB of nothing but repeated [http] listening on [::]:8742 — no exit reason, no signal, no diagnostic, 254 times. RSS was 2.34 GB at 5m33s of uptime. Not this PR's scope, but it is the same defect class §8.1 is about: a process ending without the reason being derivable from anything it emitted.

Follow-up: the restart is now explained, and it is not related to this work. `ai.neuron.engram.plist` sets `KeepAlive: true`, and the engram has been restarting on a **~10 minute cadence since 2026-08-13** — 254 restarts recorded in `~/.neuron/logs/engram-remerge.log` (one line per process start), first `2026-08-13T19:37:32Z`, last `2026-08-16T18:16:30Z`. The cadence is unchanged across the window that contains my session, so the PID change from 23583 to 35917 is one tick of a cycle that predates this branch. Withdrawing 'unexplained'; the restart is accounted for. The claim that this work issued no request or signal to `:8742` or `:7770` is unchanged and still rests on the probe log. Separately worth someone's attention, since it surfaced here: nothing logs **why** it exits. `~/.neuron/logs/engram.log` is 2.7 MB of nothing but repeated `[http] listening on [::]:8742` — no exit reason, no signal, no diagnostic, 254 times. RSS was 2.34 GB at 5m33s of uptime. Not this PR's scope, but it is the same defect class §8.1 is about: a process ending without the reason being derivable from anything it emitted.
will.anderson merged commit 0ee82d9e91 into dev 2026-08-16 20:49:15 +00:00
Sign in to join this conversation.