7a1501d097
El SDK CI - dev / build-and-test (pull_request) Failing after 3m59s
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.
469 lines
31 KiB
C
469 lines
31 KiB
C
/* engram_cognition.h — THE ONE OPERATION.
|
||
*
|
||
* The buildable form of the "cognition is one operation" theory (design doc
|
||
* engram/spec/cognitive-architecture.design.md; memory bdc8a488 / d582a766).
|
||
*
|
||
* Cognition is ONE operation — think — a directed traversal-READ of the geometry
|
||
* from an anchor, steered by a learned STANCE, whose output is a GRADIENT (a
|
||
* direction + spread over the geometry), never a point. The named faculties
|
||
* (reason / induce / abduce / analogy / relate / plan / ground) are human LABELS
|
||
* on regions of think's steering space: each faculty == { think + a named stance }.
|
||
* Collapse-to-a-point happens only at EXPRESSION (a separate faculty), never in think.
|
||
*
|
||
* NAMING (Will's directive): the surface verbs name the cognitive ACT being
|
||
* performed (think / reason / induce / ground / verify), not the internal function
|
||
* shape. The single frozen primitive underneath every faculty is engram_think,
|
||
* which composes over engram_reason_point_fit + the §5 geo-algebra. Those never
|
||
* learn. Only the STANCE learns.
|
||
*
|
||
* "Stance" is the theory's steering PRIOR, deliberately named distinctly: in this
|
||
* codebase the token "prior" already means previous-VERSION (supersession). A
|
||
* Stance is a learnable bias/disposition over the geometry — which axes matter,
|
||
* which way pays off, plus a calibrated track record — attached to a faculty-label
|
||
* and a region, and grounded-for-whom.
|
||
*
|
||
* PURE + (mostly) READ-ONLY, stdlib + libm only. think() and the warp are pure
|
||
* over their inputs. Persistence (Stance <-> StoreNode, edge grounding vectors) is the
|
||
* only part that touches the store, and it is additive / supersede / tombstone —
|
||
* never mutate-in-place, never delete. It NEVER touches the live daemon: all
|
||
* offline against a scratch store, per the design's rails.
|
||
*/
|
||
#ifndef ENGRAM_COGNITION_H
|
||
#define ENGRAM_COGNITION_H
|
||
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
#include "engram_geometry.h"
|
||
#include "engram_reason.h"
|
||
#include "engram_store.h"
|
||
|
||
/* Max principal axes a stance warps (matches GeoParams.top_axes default budget). */
|
||
#define COG_MAX_AXES 32
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════
|
||
* §1 GeoGradient — the OUTPUT of think. A direction + spread over the geometry,
|
||
* plus the calibrated confidence and the read it was computed against. NOT a point.
|
||
* A spiked gradient (spread→0) = "exact" (deduction); a spread gradient = "fuzzy"
|
||
* (prediction). The gradient is ALSO the next steering direction (closed-loop flow).
|
||
* ═══════════════════════════════════════════════════════════════════════════ */
|
||
typedef struct {
|
||
int dim;
|
||
float* direction; /* unit steering vector in the anchor's frame (owned) */
|
||
double spread; /* 0 = spiked/exact ... large = diffuse/fuzzy */
|
||
double confidence; /* calibrated, from the stance's track record (reliab.) */
|
||
double magnitude; /* THIS read's own membership/fit estimate ∈(0,1].
|
||
* The scalar an expression faculty would SAMPLE; kept
|
||
* on the gradient but never used AS a decision by think.*/
|
||
const char* anchor_id; /* borrowed: the vantage this was read from */
|
||
int n_support; /* neighborhood members that shaped the read */
|
||
const char* stance_id; /* borrowed: which stance steered this (provenance) */
|
||
} GeoGradient;
|
||
|
||
void engram_gradient_free(GeoGradient* g);
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════
|
||
* §2 Stance — the learnable steering prior, as a first-class object. In memory
|
||
* here; persisted as a StoreNode (node_type "Stance") via cog_stance_*serialize.
|
||
*
|
||
* warp: axis_gain[] per-principal-axis multiplier on extents (which axes
|
||
* matter — gain>1 WIDENS an axis so it penalizes less);
|
||
* bias_dir[] a steering-direction seed in the region's frame;
|
||
* scalars faculty constants this stance overrides (ext_floor, etc).
|
||
* calibration: the track record — the ONLY thing the loop (§4) updates
|
||
* besides warp: n_trials, a Brier accumulator, reliability
|
||
* (→ GeoGradient.confidence), and an EMA error.
|
||
* keystone: if set, the correspondence-loop MUST NEVER write warp or
|
||
* calibration — read-mostly (self / values). §6 metastability.
|
||
* ═══════════════════════════════════════════════════════════════════════════ */
|
||
typedef struct {
|
||
char* id; /* stance node id (owned) */
|
||
char* faculty; /* the act this stance serves: "induce"|"relate"|... */
|
||
char* anchor_region; /* node/neighborhood id this stance is attached to */
|
||
char* for_whom; /* observer id — grounding is relational (NULL=global) */
|
||
int keystone; /* 1 = read-mostly, loop never writes it (§6) */
|
||
|
||
int dim; /* embedding dim of the region */
|
||
int n_axes; /* how many axis_gain entries are live (<= COG_MAX_AXES)*/
|
||
double axis_gain[COG_MAX_AXES]; /* per-axis extent multipliers (init 1.0) */
|
||
float* bias_dir; /* dim floats, steering seed (owned; NULL = none) */
|
||
double ext_floor; /* faculty scalar: the extent floor (init 1.0) */
|
||
double drop_frac; /* faculty scalar (causal): confound drop (init 0.5) */
|
||
double assoc_floor; /* faculty scalar (causal): assoc floor (init 0.2) */
|
||
|
||
/* calibration / track record */
|
||
int64_t n_trials;
|
||
double brier_sum; /* Σ (p − y)² */
|
||
double reliability; /* calibrated ∈[0,1] → GeoGradient.confidence */
|
||
double ema_error; /* EMA of per-trial error */
|
||
double last_error;
|
||
} CogStance;
|
||
|
||
/* Initialize a neutral stance (all gains 1.0, default scalars, reliability 0.5).
|
||
* dim/n_axes taken from the region descriptor. faculty/id/for_whom are copied. */
|
||
int cog_stance_init(CogStance* s, const char* id, const char* faculty,
|
||
const char* anchor_region, const char* for_whom,
|
||
const GeoDescriptor* region);
|
||
void cog_stance_free(CogStance* s);
|
||
|
||
/* A stance set to today's hard-coded constants == behavioral parity with the
|
||
* pre-stance operators (axis_gain all 1.0, ext_floor default, drop_frac 0.5,
|
||
* assoc_floor 0.2). This is the FROZEN CONTROL used by the validation. */
|
||
void cog_stance_set_frozen_defaults(CogStance* s);
|
||
|
||
/* ── Serialization: Stance <-> StoreNode (compact line schema "STNC1", mirroring
|
||
* the reify "GEO1" precedent). Additive; the node's importance field caches the
|
||
* reliability readout. Round-trips exactly (reboot-prove). ──────────────────── */
|
||
char* cog_stance_to_metadata(const CogStance* s); /* owned string */
|
||
int cog_stance_to_node(const CogStance* s, StoreNode* out);/* fills a StoreNode */
|
||
int cog_stance_from_node(const StoreNode* n, CogStance* out);/* parse STNC1 */
|
||
|
||
#define COG_STANCE_NODE_TYPE "Stance"
|
||
#define COG_STANCE_META_MAGIC "STNC1"
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════
|
||
* §1.2 think — the ONE operation. Frozen procedure over three steps:
|
||
* 1. re-origin on the anchor point (the vantage; the manifold is the read
|
||
* neighborhood, passed as `region`);
|
||
* 2. fit the anchor under the stance's WARP (engram_reason_point_fit with the
|
||
* axis extents multiplied by axis_gain and ext_floor substituted);
|
||
* 3. emit a GRADIENT: direction = the warped steepest-descent that reduces the
|
||
* fit distance (the "which way pays off" seed + bias_dir), spread from the
|
||
* fit distance, confidence from the stance's reliability, magnitude = the
|
||
* read's membership estimate. NO point-collapse — that is expression.
|
||
*
|
||
* `region` — the read neighborhood (built by vantage_read / geometry descriptor).
|
||
* `anchor` — the point to read FROM (dim floats). NULL = region centroid (self).
|
||
* `stance` — the steering prior. NULL = neutral (frozen defaults) => parity.
|
||
* Returns 0 and fills `out` (engram_gradient_free), <0 on error.
|
||
* ═══════════════════════════════════════════════════════════════════════════ */
|
||
int engram_think(const GeoDescriptor* region, const float* anchor,
|
||
const CogStance* stance, GeoGradient* out);
|
||
|
||
/* The warped fit itself (step 2), exposed for the loop + verifier reuse. Identical
|
||
* to engram_reason_point_fit when stance==NULL or all gains==1 && ext_floor default. */
|
||
int cog_warped_fit(const GeoDescriptor* region, const float* x,
|
||
const CogStance* stance, GeoFit* out);
|
||
|
||
/* EXPRESSION — the ONLY place a gradient collapses to a point. Samples the gradient
|
||
* off the anchor along its steering direction, scaled by (1 − spread) so a spiked
|
||
* (confident) gradient lands a definite point and a diffuse one barely moves.
|
||
* This is deliberately a SEPARATE faculty from think (§1.2, M5). */
|
||
int engram_express(const GeoGradient* g, const float* anchor, float* out_point);
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════
|
||
* §5 HOLD vs GROUND vs ASSERT. Holding is unconditional (the store gates nothing).
|
||
* Grounding is an ATTRIBUTE OF a relation — carried on the edge itself, as a
|
||
* vector (§7). The honesty floor is checked only at ASSERTION, on both axes.
|
||
* ═══════════════════════════════════════════════════════════════════════════ */
|
||
/* DELETED 2026-08-16: COG_GROUNDED_BY_RELATION and cog_ground_edge.
|
||
*
|
||
* A "grounded-by" edge models grounding as a relation BETWEEN two nodes. It is a
|
||
* property OF a relation — and it is that relation's weight. Minting a new edge
|
||
* to carry a score was the error; #147 corrected which endpoints the edge landed
|
||
* on and left the wrong idea standing. There is nothing to ground a claim
|
||
* "against" that is not already an edge, and if no edge exists the honest answer
|
||
* is that the two are not related — not a freshly minted one scoring 0.98.
|
||
* See §7 for what replaced it. */
|
||
#define COG_SALIENT_TO_RELATION "salient-to"
|
||
|
||
/* Write/refresh a salient-to edge: salience is RELATIONAL (grounded-for-whom),
|
||
* carried on the edge to the observer — not baked into the node scalar (§2.1). */
|
||
int cog_salient_edge(EngramPagedStore* s, const char* node_id,
|
||
const char* observer_id, double salience);
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════
|
||
* §4 THE REFLEXIVE CORRESPONDENCE-LOOP — the learning engine. think scores its
|
||
* OWN gradient against outcome, refines the stance on the error, and (optionally)
|
||
* writes the (gradient, outcome, error) back as self-describing geometry. This is
|
||
* the dormant verifier turned INWARD.
|
||
*
|
||
* grade(1) SELF-CONSISTENCY (no external world-labels): the outcome is what the
|
||
* geometry itself says — the membership determined by the region's SIGNAL subspace
|
||
* (the axes reality actually weights). The stance's cheap warped read is graded
|
||
* against that geometric truth; error refines the warp so the read corresponds.
|
||
* ═══════════════════════════════════════════════════════════════════════════ */
|
||
typedef struct {
|
||
double correspondence; /* ∈[0,1]: 1 − |p − y| for this trial */
|
||
double error; /* 1 − correspondence */
|
||
double brier; /* running mean (p − y)² across the stance's trials */
|
||
double reliability; /* the stance's current calibrated reliability */
|
||
int wrote_keystone; /* 1 iff a keystone update was BLOCKED (safety audit) */
|
||
} CogBeatResult;
|
||
|
||
/* One correspondence beat for ONE trial:
|
||
* think(region, anchor, stance) -> gradient (a PREDICTION, ungrounded)
|
||
* outcome y := grade-1 self-consistency target (in [0,1])
|
||
* error = |magnitude − y|; refine stance.warp + calibration on the error
|
||
* (bounded step; NEVER writes a keystone stance)
|
||
* `learn`==0 grades WITHOUT updating (the frozen-control path). `max_step` bounds
|
||
* the per-beat warp change (metastability; §6). Returns 0 / <0. */
|
||
int engram_correspondence_beat(const GeoDescriptor* region, const float* anchor,
|
||
double outcome_y, CogStance* stance,
|
||
int learn, double max_step, CogBeatResult* out);
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════
|
||
* §6 METASTABILITY. Keystones (self/values) are read-mostly: the loop reads but
|
||
* never writes them. Mark by stance flag or by a keystone-id set the loop consults.
|
||
*
|
||
* SUPERSEDED BY §7's PROVENANCE CONSTRAINT (2026-08-16). The keystone flag is a
|
||
* PERMISSION: it asks who the target is, not where the evidence came from. That
|
||
* is censorship, and it costs the ability to ever ground the self (spec
|
||
* correspondence-and-censorship.md §0/§2). The constraint that actually protects
|
||
* a reference frame is cog_grounding_downstream: a region may not be calibrated
|
||
* by evidence downstream of itself. These declarations remain only so existing
|
||
* call sites keep compiling; nothing in the grounding path consults them.
|
||
* ═══════════════════════════════════════════════════════════════════════════ */
|
||
typedef struct { const char** ids; int n; } CogKeystoneSet;
|
||
int cog_is_keystone(const CogKeystoneSet* ks, const CogStance* s);
|
||
|
||
/* ═══════════════════════════════════════════════════════════════════════════
|
||
* §7 GROUNDING IS THE EDGE'S WEIGHT, AND THE WEIGHT IS A VECTOR
|
||
* (2026-08-16; spec correspondence-and-censorship.md §2–§6 @ 2b7e4ba.)
|
||
*
|
||
* THE MODEL. Grounding is not a subsystem, a score, or a relation BETWEEN nodes.
|
||
* It is an attribute OF a relation. The graph already IS the grounding structure:
|
||
* every edge is a grounded relation, and what that relation is worth is carried
|
||
* on the edge itself. Three things follow, and each DELETES rather than adds:
|
||
*
|
||
* 1. `grounded-by` as a relation type does not exist, and cog_ground_edge is
|
||
* gone. Minting an edge to hold a score models grounding as a relation
|
||
* between nodes when it is a property of a relation. #147 corrected which
|
||
* endpoints that edge landed on and left the wrong idea standing.
|
||
* 2. There is no observer, and no sampling rate. Change is not a consequence of
|
||
* use — it IS use, the way potentiation is the firing rather than something
|
||
* that reads the firing and writes a weight. So no supervisor compares a
|
||
* value to a threshold and decides to persist.
|
||
* 3. Between two recorded versions the trajectory is not unknown. Decay is a
|
||
* pure function of the last recorded point and elapsed time, so it is
|
||
* ANALYTIC: store the point, read the curve.
|
||
*
|
||
* WHAT IS *NOT* HERE, DELIBERATELY. An earlier draft of the spec posed "a graph
|
||
* predicate for evidence downstream of itself" as the hard problem, and this file
|
||
* briefly contained one. It is withdrawn. Non-circularity is TEMPORAL, not
|
||
* topological: you cannot recalibrate the ruler while measuring with it, so you
|
||
* do it when you are not using the frame to act. Reachability could never have
|
||
* worked — measured on the live store, reachability from the self region over
|
||
* all relations reaches 89.2% of the graph (10,580 of 11,861 nodes) and 16.0%
|
||
* over hebbian/semantic relations alone, so the predicate marks essentially all
|
||
* evidence tainted and the constraint degenerates into the total block that
|
||
* censorship started as. Nothing replaces it here; the independence is a fact
|
||
* about engagement, owned by the dreamer, not a fact about the graph.
|
||
*
|
||
* ═══════════════════════════════════════════════════════════════════════════
|
||
* §7.1 THE VECTOR
|
||
*
|
||
* The test for a real dimension is whether it can move independently of the
|
||
* others. Five can, and each maps onto substrate that already exists:
|
||
*
|
||
* factual correspondence with evidence. [GRD1]
|
||
* relational correspondence with values — min over THIRTEEN
|
||
* value regions, carrying the binding value's NAME. [GRD1]
|
||
* associative co-activation frequency. This is the edge's `hebb`
|
||
* field with its existing dynamics — NOT a new one.
|
||
* Independent by construction: every superstition is
|
||
* a strong association with no factual grounding.
|
||
* polarity SIGNED. Near zero means "no support"; NEGATIVE means
|
||
* "this actively contradicts". The edge's `inhibitory`
|
||
* bit is exactly this distinction crushed to one bit,
|
||
* and is carried forward as the seed value. [GRD1]
|
||
* provenance observed / inferred / told / imprinted. Categorical,
|
||
* and load-bearing: it governs what the relation is
|
||
* entitled to. [GRD1]
|
||
*
|
||
* Plus a TIMESTAMP, which is what turns the supersession chain into a time
|
||
* series of vectors rather than a series of numbers.
|
||
*
|
||
* DERIVED, THEREFORE NEVER STORED. Confidence (high grounding AND low
|
||
* volatility), recency (decay read off the curve), staleness (grounding fallen
|
||
* below its floor), volatility (the derivative of a series nothing destroyed).
|
||
* Storing confidence separately is how `confidence: 0.5` ends up sitting beside
|
||
* a zero direction vector, asserting something nothing computed. Every field in
|
||
* CogGrounding below is marked STORED or DERIVED, and the serializer writes
|
||
* only the STORED ones.
|
||
*
|
||
* THE VALUES REFERENCE IS THIRTEEN REGIONS AND THE AGGREGATE IS MIN.
|
||
* Measured on the live store: the values root kn-5b606390 `contains` exactly 13
|
||
* value nodes; pairwise centroid cosine among their regions is min 0.1525,
|
||
* mean 0.5199, median 0.5282, max 0.9278 — they demonstrably do not form one
|
||
* region. Against a single union region the individual values sit at cosine
|
||
* 0.38..0.89, with constraints-as-freedom at 0.3812 and change-is-the-signal at
|
||
* 0.4677, so a union centroid under-represents precisely the values a claim is
|
||
* most likely to be measured against. MIN rather than MEAN because a mean lets
|
||
* strong agreement with twelve values mask a violation of the thirteenth, which
|
||
* is the mechanism of rationalization; min yields a binding constraint with a
|
||
* NAME attached rather than a score.
|
||
*
|
||
* TRAVERSAL CONDUCTS ON FACTUAL; ASSERTION REQUIRES BOTH. If activation
|
||
* conducted on relational weight, Neuron could not follow a chain of reasoning
|
||
* to a conclusion he then rejects — censorship arriving through the spreading
|
||
* rule. The gap between reachable and assertable is where the wide
|
||
* factual/relational angles live, and that gap is the interesting part.
|
||
* ═══════════════════════════════════════════════════════════════════════════ */
|
||
|
||
/* ── The one decay model (moved here from el_runtime.c so that node decay and
|
||
* edge-grounding decay are a single implementation with a single set of
|
||
* constants, rather than a model and a parallel copy of it). Half-life scales
|
||
* with how established the thing is: T_eff = T_HALF · (1 + ln(1 + reinforcements)).
|
||
* The floor is a preference, not a cliff — max penalty for age alone is 4x.
|
||
* `lambda_override` > 0 replaces the default rate; 0 means use the default. */
|
||
#define COG_T_HALF_HOURS 168.0
|
||
#define COG_DECAY_LAMBDA 0.693147
|
||
#define COG_DECAY_FLOOR 0.25
|
||
double cog_decay_factor(int64_t age_ms, double reinforcements, double lambda_override);
|
||
|
||
/* The compact vector block carried in the edge's own metadata. Line schema, same
|
||
* precedent as STNC1 / GEO1. Metadata the edge already carried is preserved
|
||
* verbatim ahead of the magic line. */
|
||
#define COG_GROUNDING_META_MAGIC "GRD1"
|
||
|
||
/* Provenance class — categorical, and it governs what the relation is entitled
|
||
* to. A change of class is inherently significant and needs no threshold,
|
||
* because told → observed is a categorical upgrade, not a drift. */
|
||
typedef enum {
|
||
COG_PROV_UNSET = 0,
|
||
COG_PROV_OBSERVED = 1,
|
||
COG_PROV_INFERRED = 2,
|
||
COG_PROV_TOLD = 3,
|
||
COG_PROV_IMPRINTED = 4
|
||
} CogProvClass;
|
||
const char* cog_prov_name(CogProvClass p);
|
||
CogProvClass cog_prov_parse(const char* s);
|
||
|
||
typedef struct {
|
||
int present; /* 1 iff the edge carries a GRD1 block */
|
||
|
||
/* ── STORED: the vector, as it stood at `ts` ─────────────────────────────── */
|
||
double factual; /* correspondence with evidence */
|
||
double relational; /* min over the thirteen value regions */
|
||
double associative; /* co-activation frequency — mirrors edge->hebb */
|
||
double polarity; /* SIGNED support; <0 = actively contradicts */
|
||
CogProvClass prov; /* observed / inferred / told / imprinted */
|
||
int64_t ts; /* when this version was recorded (ms) */
|
||
int64_t seq; /* supersession sequence number */
|
||
double reinforcements; /* uses folded into this version */
|
||
char binding_value[128]; /* the argmin value — the conflict's NAME */
|
||
/* the two gradients as frame-independent signed projections, plus the angle
|
||
* between them in full R^dim. These are part of the JOINT STATE a decision
|
||
* saw, not a convenience: near +1 evidence and values push the same way; at
|
||
* or below 0 the relation is factually supported and relationally wrong. */
|
||
double fac_proj, rel_proj, cos_angle;
|
||
int agreement; /* sign(cos_angle): +1 / 0 / −1 */
|
||
double floor_at_record, rel_floor_at_record;
|
||
char prev_edge[192]; /* the version this superseded ("" if first) */
|
||
|
||
/* ── DERIVED at read time. NEVER serialized. ─────────────────────────────── */
|
||
int64_t age_ms; /* recency: now − ts */
|
||
double decay; /* cog_decay_factor over that age */
|
||
double factual_now; /* factual · decay */
|
||
double relational_now;
|
||
double associative_now;
|
||
int stale; /* grounding fallen below its floor */
|
||
} CogGrounding;
|
||
|
||
/* Read an edge's vector as of `now_ms`. Pure — never writes. An edge with no
|
||
* GRD1 block still has an associative strength (its accrued hebb) and a polarity
|
||
* (its signed authored weight); `present` says whether the grounding dimensions
|
||
* have ever been established, and an unestablished dimension is reported as such
|
||
* rather than defaulted to a passing value. */
|
||
int cog_grounding_parse(const StoreEdge* e, int64_t now_ms, CogGrounding* out);
|
||
|
||
/* Serialize the STORED half of the vector, preserving pre-existing non-GRD1
|
||
* metadata. Returns an owned string. Derived fields are not written. */
|
||
char* cog_grounding_metadata(const char* base_meta, const CogGrounding* g);
|
||
|
||
/* ── §7.2 CONSOLIDATION-GATED SUPERSESSION ──────────────────────────────────
|
||
*
|
||
* Supersession is not recording — it is CONSOLIDATION, gated by salience, which
|
||
* is why you remember the argument and not the commute. Significance is
|
||
* evaluated PER-DIMENSION but the record is the WHOLE VECTOR: any dimension
|
||
* moving enough to matter triggers a supersession, and the new version captures
|
||
* every dimension as it stood at that instant. Versioning axes independently
|
||
* would make the joint state unreconstructable, and the joint state is the point
|
||
* — it is what makes "stayed true, became wrong" visible as an event (factual
|
||
* holding steady across versions while relational degrades).
|
||
*
|
||
* There is deliberately no epsilon in this enum or in the function that computes
|
||
* it. Every test is a floor crossing or a sign change, both exact. Two of them
|
||
* are INHERENTLY significant because they are discrete state changes rather than
|
||
* drift, and those bypass the salience gate entirely. */
|
||
typedef enum {
|
||
COG_SIG_NONE = 0, /* nothing decision-relevant moved — DO NOT RECORD */
|
||
COG_SIG_FIRST_RECORD = 1, /* no prior version exists */
|
||
COG_SIG_POLARITY_FLIP = 2, /* INHERENT: support ↔ contradiction, or ignorance
|
||
* ↔ either. A discrete change of state. */
|
||
COG_SIG_PROVENANCE_CHANGE = 3, /* INHERENT: told → observed is a categorical
|
||
* upgrade in what the relation is entitled to. */
|
||
COG_SIG_FACTUAL_FLOOR = 4, /* crossed the assert floor, factual axis */
|
||
COG_SIG_RELATIONAL_FLOOR = 5, /* crossed the assert floor, relational axis */
|
||
COG_SIG_AGREEMENT_FLIP = 6, /* factual/relational agreement changed sign */
|
||
COG_SIG_DIRECTION_REVERSAL = 7 /* a gradient reversed direction */
|
||
} CogSignificance;
|
||
|
||
CogSignificance cog_grounding_significant(const CogGrounding* prev,
|
||
const CogGrounding* now,
|
||
double floor, double rel_floor);
|
||
const char* cog_significance_name(CogSignificance s);
|
||
/* 1 iff this reason is a discrete state change that consolidates regardless of
|
||
* salience (polarity flip, provenance change, first record). */
|
||
int cog_significance_inherent(CogSignificance s);
|
||
|
||
/* ── §7.3 RECORDING: supersession of the EDGE, never an overwrite ────────────
|
||
* Writes version seq+1 as a NEW edge record with the same endpoints and relation
|
||
* and id "<root>#<seq+1>", carrying a GRD1 `p` pointer to its predecessor. The
|
||
* predecessor is never touched. The chain IS the trajectory: not only what the
|
||
* grounding is but which way it has been moving and how fast — a derivative
|
||
* obtained for free from immutability, because the points were never destroyed.
|
||
* Returns the version written (>=1), or <0 on error. */
|
||
int cog_grounding_record(EngramPagedStore* s, const StoreEdge* base,
|
||
const CogGrounding* g, char* out_id, size_t out_id_cap);
|
||
|
||
/* Walk forward from a base edge id to its newest recorded version. Point reads
|
||
* only; consolidation is gated, so the chain is short. Returns the highest
|
||
* version found (0 = the base record is the only one). */
|
||
int cog_grounding_head(EngramPagedStore* s, const char* base_id,
|
||
StoreEdge* out, int max_versions);
|
||
|
||
/* VOLATILITY — derived, never stored: the mean absolute per-version change of a
|
||
* dimension across the recorded chain. Feeds the equally-derived `confidence`
|
||
* (high grounding AND low volatility), which is likewise never stored. */
|
||
typedef struct {
|
||
int n_versions;
|
||
double factual_volatility;
|
||
double relational_volatility;
|
||
double factual_drift; /* signed: newest − oldest */
|
||
double relational_drift;
|
||
int stayed_true_became_wrong; /* factual steady while relational degraded */
|
||
} CogTrajectory;
|
||
int cog_grounding_trajectory(EngramPagedStore* s, const char* base_id,
|
||
int64_t now_ms, CogTrajectory* out);
|
||
|
||
/* ── §7.4 ASSERTION GATES ON BOTH FLOORS ────────────────────────────────────
|
||
* A well-evidenced claim must not earn the right to be asserted regardless of
|
||
* whether it means the right thing. `may_assert` requires the decayed factual
|
||
* grounding to clear `floor` AND the decayed relational grounding to clear
|
||
* `rel_floor`. A relation whose relational axis has never been established does
|
||
* not pass by default — it is reported unestablished and refused, because
|
||
* defaulting it to passing is exactly the exemption §0 forbids. Traversal is
|
||
* untouched: activation still conducts on the factual/associative side, so a
|
||
* relation can remain thinkable while ceasing to be assertable. */
|
||
typedef struct {
|
||
int may_assert;
|
||
int found; /* any relation at all on this claim */
|
||
int relational_established;
|
||
int still_held; /* DERIVED: node present and not tombstoned */
|
||
double factual; /* best decayed factual grounding */
|
||
double relational; /* the SAME edge's relational axis, not a max */
|
||
double polarity;
|
||
double cos_angle;
|
||
int agreement;
|
||
CogProvClass prov;
|
||
char best_edge[192];
|
||
char binding_value[128];
|
||
int n_edges;
|
||
} CogAssertion;
|
||
int cog_assert_two_axis(EngramPagedStore* s, const char* claim_id,
|
||
double floor, double rel_floor, int64_t now_ms,
|
||
CogAssertion* out);
|
||
|
||
#endif /* ENGRAM_COGNITION_H */
|