Compare commits

...

7 Commits

Author SHA1 Message Date
Neuron 688f24b4c1 ingest: name the inversion, and correct the worked example to decomposition
El SDK CI - dev / build-and-test (pull_request) Failing after 14m6s
ingest.el's transduce() was renamed to transduce_manifold() earlier the same
day on the reasoning that it 'was never signal->geometry -- it chunks
already-extracted content and PACKS it into a node+edge manifold, one layer up,
and it had taken the name that belongs to the primitive underneath it.'

That reasoning was backwards. Producing a node+edge manifold is not a layer
above transduction, it IS transduction. Signal -> one vector is the operation
underneath, and its name is geometry. The layer doing it right was renamed out
of the way so the layer doing it wrong could have the name.

With the primitive corrected to return a Manifold, the two layers do the same
kind of thing and the inversion dissolves. What is left is a real distinction
about MODALITY, not layering: transduce() dispatches to a realizer that knows
its modality and can name its components; transduce_bytes() is the
opaque-bytes realizer, the decomposition available to a reader that knows
nothing about what it is reading. It still yields components and relations,
which is why it is transduction and not packing -- it just cuts on byte
boundaries, so its components are positional rather than meaningful. That is a
limitation of this realizer, not the definition of the operation.

Renamed by modality rather than demoted by layer. A distinct symbol is still
mechanically required: reusing transduce here is a conflicting-types error the
moment ingest.c links el_runtime.c.

lang/examples/transduce.el asserted #144's contract and would now fail, so it
is replaced by the decomposition worked example: transduce a chord, persist the
five components and six relations as real nodes and edges, read each part's
geometry back off its own node, and ground one part while its sibling is
demonstrably untouched.
2026-08-16 15:50:00 -05:00
Neuron d777936ee4 runtime: transduction decomposes a signal, it does not convert it
#144 moved transduction into the language and got the dispatch right. It got
the result type wrong: transduce(signal, modality) -> Geometry yields one
vector per signal, and one vector is a fingerprint. A fingerprint can be
matched and ranked; that is all. It cannot be decomposed, cannot have one part
grounded while another is not, and cannot be contradicted in one part while
holding in another, because it has no parts.

A song is not a point. It decomposes into pitch, interval, rhythm, harmonic
function -- components, each with its own geometry, plus the relations among
them. The song IS the structure of the relations.

transduce now returns a Manifold: named components carrying geometry, and
typed weighted relations between them. Signal in, subgraph out.

Components are addressed by key, never by index, because the key is what
survives persistence -- a component becomes a node and is separately groundable
precisely because it is separately named. Relation weight IS the grounding
(correspondence-and-censorship.md 1), so a realizer's relations arrive already
grounded and there is no score computed beside them.
2026-08-16 15:50:00 -05:00
will.anderson 4a57b4faa8 Merge pull request 'docs: the builtin recipe never required a test' (#154) from docs/builtin-recipe-gate into dev
El SDK CI - dev / build-and-test (push) Failing after 3m53s
2026-08-16 20:49:21 +00:00
will.anderson 0ee82d9e91 Merge pull request 'Grounding is the edge's weight, and the weight is a vector' (#150) from feat/grounding-gradient into dev
El SDK CI - dev / build-and-test (push) Failing after 3m43s
2026-08-16 20:49:12 +00:00
will.anderson 9526bda507 Merge pull request 'engram: expose the geometry so the frame can be verified' (#156) from fix/geometry-readable into dev
El SDK CI - dev / build-and-test (push) Failing after 4m7s
2026-08-16 20:49:07 +00:00
Neuron fe820928b0 docs: the builtin recipe never required a test
El SDK CI - dev / build-and-test (pull_request) Failing after 10m55s
lang/AGENTS.md:71-77 gives four steps for adding a C builtin and ends at
'confirm the self-host fixpoint is byte-identical'. No step asks for a test.
The only 'verify' in the file is that fixpoint, which proves the COMPILER
REPRODUCES ITSELF and says nothing about whether the builtin works — so the
recipe reads as complete while having checked nothing about the thing just
added.

Measured on 2026-08-16: engram_node_set_emb, engram_curiosity_json and
dream_set_handler were all added in a single session with zero tests, by an
agent following this recipe. Separately a UTF-8 fix was written and tested
and THE TEST PASSED ON THE UNPATCHED BUILD — the real defect was elsewhere,
and only building the pre-fix binary exposed it. Without a negative control
that fix would have merged as verified.

Adds step 5 with the two failure shapes actually encountered: a test that
never exercises the change (a route default bypassed the code under test),
and an induction that loses a race (curl --max-time left BOTH builds alive;
only SO_LINGER 0, a real RST, reproduced it). Plus the port-binding check,
because a stale instance answering has silently produced false results here
more than once and pkill -f does not reliably match argv './engram'.

Documentation only. Does not touch the (a) split-the-C / (b) close-the-
compiler-gap question, which is a separate decision.
2026-08-16 13:53:08 -05:00
Neuron 7a1501d097 Grounding is the edge's weight, and the weight is a vector
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.
2026-08-16 13:18:50 -05:00
11 changed files with 2527 additions and 499 deletions
+33 -1
View File
@@ -1134,6 +1134,11 @@ fn route_faculty(path: String, faculty: String) -> String {
fn route_boundary_proof(method: String, path: String, body: String) -> String { fn route_boundary_proof(method: String, path: String, body: String) -> String {
return "{\"op\":\"boundary_proof\",\"body_instrumentation\":\"none\",\"seam\":\"@manager -> engram_boundary_beat auto-injected\"}" return "{\"op\":\"boundary_proof\",\"body_instrumentation\":\"none\",\"seam\":\"@manager -> engram_boundary_beat auto-injected\"}"
} }
// GROUNDING: an attribute of the RELATION, and the relation's weight is a
// VECTOR (factual, relational, associative, polarity, provenance, timestamp).
// /api/ground READS it it never writes. /api/ground/record is the write,
// named as one, and it consolidates only on a consequential + salient move.
// /api/ground/trajectory reads the supersession chain as a time series.
fn route_ground(method: String, path: String, body: String) -> String { fn route_ground(method: String, path: String, body: String) -> String {
let claim: String = json_get_string(body, "claim") let claim: String = json_get_string(body, "claim")
let evidence: String = json_get_string(body, "evidence") let evidence: String = json_get_string(body, "evidence")
@@ -1142,12 +1147,31 @@ fn route_ground(method: String, path: String, body: String) -> String {
if str_eq(evidence, "") { return err_json("missing evidence") } if str_eq(evidence, "") { return err_json("missing evidence") }
return engram_ground_json(claim, evidence, for_whom) return engram_ground_json(claim, evidence, for_whom)
} }
fn route_ground_record(method: String, path: String, body: String) -> String {
let claim: String = json_get_string(body, "claim")
let evidence: String = json_get_string(body, "evidence")
let provenance: String = json_get_string(body, "provenance")
let floor: String = json_get_string(body, "floor")
if str_eq(claim, "") { return err_json("missing claim") }
if str_eq(evidence, "") { return err_json("missing evidence") }
return engram_ground_record_json(claim, evidence, provenance, floor)
}
fn route_ground_trajectory(method: String, path: String, body: String) -> String {
let claim: String = query_param(path, "claim")
let evidence: String = query_param(path, "evidence")
if str_eq(claim, "") { return err_json("missing claim") }
if str_eq(evidence, "") { return err_json("missing evidence") }
return engram_ground_trajectory_json(claim, evidence)
}
fn route_assert(method: String, path: String, body: String) -> String { fn route_assert(method: String, path: String, body: String) -> String {
let claim: String = query_param(path, "claim") let claim: String = query_param(path, "claim")
if str_eq(claim, "") { return err_json("missing claim") } if str_eq(claim, "") { return err_json("missing claim") }
let for_whom: String = query_param(path, "for_whom") let for_whom: String = query_param(path, "for_whom")
let floor: String = query_param(path, "floor") let floor: String = query_param(path, "floor")
return engram_assert_json(claim, for_whom, floor) // Both floors. A well-evidenced claim does not earn the right to be asserted
// regardless of whether it means the right thing. rel_floor defaults to floor.
let rel_floor: String = query_param(path, "rel_floor")
return engram_assert_json(claim, for_whom, floor, rel_floor)
} }
fn route_attend(method: String, path: String, body: String) -> String { fn route_attend(method: String, path: String, body: String) -> String {
let node: String = json_get_string(body, "node") let node: String = json_get_string(body, "node")
@@ -1904,6 +1928,14 @@ fn handle_request(method: String, path: String, body: String) -> String {
if str_eq(method, "GET") && str_starts_with(clean, "/api/plan") { if str_eq(method, "GET") && str_starts_with(clean, "/api/plan") {
return route_faculty(path, "plan") return route_faculty(path, "plan")
} }
// Order matters: the more specific paths must be tested before the /api/ground
// prefix match below, which would otherwise swallow them.
if str_eq(method, "POST") && str_starts_with(clean, "/api/ground/record") {
return route_ground_record(method, path, body)
}
if str_eq(method, "GET") && str_starts_with(clean, "/api/ground/trajectory") {
return route_ground_trajectory(method, path, body)
}
if str_eq(method, "POST") && str_starts_with(clean, "/api/ground") { if str_eq(method, "POST") && str_starts_with(clean, "/api/ground") {
return route_ground(method, path, body) return route_ground(method, path, body)
} }
+40
View File
@@ -0,0 +1,40 @@
#!/bin/sh
# Build + RUN the §7 GROUNDING-VECTOR tests (engram_cognition.c): the one decay
# model, the consequence gate, and the stored/derived split. Closed-form
# constructed cases — no server, no store, no network. Pure C11 (stdlib + libm).
# Standalone — NOT folded through elc. Two passes:
# 1. PERF — optimised (-O2, no sanitizer): the functional gate.
# 2. SAFETY — ASan + UBSan on the same suite.
#
# NEGATIVE CONTROL (invariant §8.6 — no test without one). Every symbol this
# suite exercises (cog_decay_factor, cog_grounding_significant,
# cog_significance_inherent, CogGrounding, CogProvClass) is introduced by the
# change under test, so the suite does not COMPILE against the pre-change source.
# To reproduce:
# git show origin/dev:lang/runtime/engram_cognition.h > /tmp/pre/engram_cognition.h
# git show origin/dev:lang/runtime/engram_cognition.c > /tmp/pre/engram_cognition.c
# cc -I/tmp/pre engram/test/test_grounding_vector.c /tmp/pre/engram_cognition.c ...
# => error: unknown type name 'CogGrounding'; no binary produced.
set -e
HERE=$(cd "$(dirname "$0")" && pwd)
RT="$HERE/../../lang/runtime"
CC=${CC:-cc}
SRC="$HERE/test_grounding_vector.c $RT/engram_cognition.c $RT/engram_reason.c $RT/engram_geometry.c $RT/engram_store.c $RT/engram_vindex.c"
WARN="-std=c11 -Wall -Wextra"
# engram_store.c declares emit_log as a WEAK symbol and null-checks it, which is
# how a test links the store without the EL runtime. Darwin's ld does not resolve
# an undefined weak symbol at static-link time, so it must be allowed explicitly.
# (The pre-existing runners in this directory — run_verify_tests.sh among them —
# do not do this and therefore fail to link on macOS. Unrelated to this change.)
LDX=""
[ "$(uname -s)" = "Darwin" ] && LDX="-Wl,-U,_emit_log"
TMP=$(mktemp -d)
echo "### PASS 1: PERF (optimised, un-sanitised) — functional gate"
$CC $WARN -O2 -I"$RT" $SRC -lm -lpthread $LDX -o "$TMP/perf"
"$TMP/perf"
echo
echo "### PASS 2: SAFETY (ASan/UBSan)"
$CC $WARN -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer -I"$RT" $SRC -lm -lpthread $LDX -o "$TMP/safe"
ASAN_OPTIONS=${ASAN_OPTIONS:-detect_leaks=0} UBSAN_OPTIONS=halt_on_error=1 "$TMP/safe"
+176
View File
@@ -0,0 +1,176 @@
/* test_grounding_vector.c — deterministic tests for §7: the one decay model, the
* consequence gate, and the stored/derived split. Links engram_cognition.c
* directly; no server, no store, no network. See run_grounding_vector_tests.sh.
*
* NEGATIVE CONTROL (invariant §8.6). Every symbol exercised here —
* cog_decay_factor, cog_grounding_significant, cog_significance_inherent,
* CogGrounding, CogProvClass — is introduced by the change under test, so this
* suite does not COMPILE against the pre-change source, let alone pass. The
* runner documents the exact reproduction.
*/
#include "engram_cognition.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
static int fails = 0;
static void ok(int cond, const char* what) {
printf(" %-62s %s\n", what, cond ? "PASS" : "*** FAIL ***");
if (!cond) fails++;
}
/* The decay formula exactly as el_runtime.c carried it before the move, so the
* refactor can be shown to be bit-identical rather than merely similar. */
static double old_engram_temporal_decay(long long age_ms, long long activation_count,
double temporal_decay_rate) {
if (age_ms <= 0) return 1.0;
double lambda = (temporal_decay_rate > 0.0) ? temporal_decay_rate : 0.693147;
double age_hours = (double)age_ms / 3600000.0;
double t_half = 168.0 * (1.0 + log(1.0 + (double)activation_count));
double factor = exp(-lambda * age_hours / t_half);
if (factor < 0.25) factor = 0.25;
return factor;
}
static CogGrounding base(void) {
CogGrounding g; memset(&g, 0, sizeof g);
g.present = 1;
g.factual = 0.60; g.relational = 0.60;
g.factual_now = 0.60; g.relational_now = 0.60;
g.associative = 0.1; g.polarity = 1.0;
g.prov = COG_PROV_TOLD;
g.fac_proj = 1.0; g.rel_proj = 1.0;
g.cos_angle = 0.9; g.agreement = 1;
g.ts = 1000; g.seq = 1; g.reinforcements = 3;
return g;
}
int main(void) {
const double F = 0.5, R = 0.5;
printf("\n== 1. DECAY IS THE ONE MODEL, AND IT IS BIT-IDENTICAL TO WHAT IT REPLACED ==\n");
{
long long ages[] = {0, 3600000LL, 86400000LL, 7*86400000LL, 30*86400000LL, 365*86400000LL};
int allsame = 1;
for (int i = 0; i < 6; i++)
for (int ac = 0; ac < 4; ac++) {
long long acs[] = {0, 1, 10, 1000};
double a = cog_decay_factor(ages[i], (double)acs[ac], 0.0);
double b = old_engram_temporal_decay(ages[i], acs[ac], 0.0);
if (a != b) allsame = 0;
}
ok(allsame, "cog_decay_factor == the pre-move engram_temporal_decay (24 pts)");
ok(cog_decay_factor(0, 0, 0.0) == 1.0, "age 0 -> no decay");
}
printf("\n DECAY OVER ELAPSED TIME (reinforcements = 0, default rate):\n");
printf(" %10s %10s\n", "elapsed", "decay");
{
struct { const char* label; long long ms; } pts[] = {
{"0", 0LL},
{"1 hour", 3600000LL},
{"1 day", 86400000LL},
{"3 days", 3LL*86400000LL},
{"7 days", 7LL*86400000LL},
{"14 days", 14LL*86400000LL},
{"30 days", 30LL*86400000LL},
{"90 days", 90LL*86400000LL},
};
double prev = 2.0; int monotone = 1;
for (unsigned i = 0; i < sizeof pts / sizeof pts[0]; i++) {
double d = cog_decay_factor(pts[i].ms, 0, 0.0);
printf(" %10s %10.6f\n", pts[i].label, d);
if (d > prev) monotone = 0;
prev = d;
}
ok(monotone, "decay is monotone non-increasing in elapsed time");
ok(fabs(cog_decay_factor(7LL*86400000LL, 0, 0.0) - 0.5) < 1e-6,
"7 days at zero reinforcements == exactly one half-life (0.5)");
ok(cog_decay_factor(7LL*86400000LL, 100, 0.0) > cog_decay_factor(7LL*86400000LL, 0, 0.0),
"reinforcement slows ageing (Lindy term)");
ok(cog_decay_factor(3650LL*86400000LL, 0, 0.0) == 0.25,
"floor is a preference not a cliff: bottoms out at 0.25");
}
printf("\n== 2. CONSEQUENCE GATE: EVERY TRIGGER, AND NO EPSILON ANYWHERE ==\n");
{
CogGrounding p = base(), n = base();
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_NONE,
"identical vectors -> NONE (a re-read must not consolidate)");
n = base(); n.factual = 0.9999; n.factual_now = 0.9999;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_NONE,
"factual 0.60 -> 0.9999 without crossing the floor -> NONE");
n = base(); n.relational = 0.5001; n.relational_now = 0.5001;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_NONE,
"relational 0.60 -> 0.5001, still above floor -> NONE");
n = base(); n.factual_now = 0.4999;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_FACTUAL_FLOOR,
"a 0.1001 drop that CROSSES the floor -> FACTUAL_FLOOR");
n = base(); n.relational_now = 0.4999;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_RELATIONAL_FLOOR,
"relational crossing its floor -> RELATIONAL_FLOOR");
n = base(); n.cos_angle = -0.05; n.agreement = -1;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_AGREEMENT_FLIP,
"agreement +1 -> -1 -> AGREEMENT_FLIP");
n = base(); n.fac_proj = -0.2;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_DIRECTION_REVERSAL,
"factual gradient reverses -> DIRECTION_REVERSAL");
n = base(); n.rel_proj = -0.2;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_DIRECTION_REVERSAL,
"relational gradient reverses -> DIRECTION_REVERSAL");
n = base(); n.polarity = -1.0;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_POLARITY_FLIP,
"support -> contradiction -> POLARITY_FLIP (inherent)");
n = base(); n.polarity = 0.0;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_POLARITY_FLIP,
"support -> ignorance (zero) -> POLARITY_FLIP: not the same state");
n = base(); n.prov = COG_PROV_OBSERVED;
ok(cog_grounding_significant(&p, &n, F, R) == COG_SIG_PROVENANCE_CHANGE,
"told -> observed -> PROVENANCE_CHANGE (inherent)");
CogGrounding fresh; memset(&fresh, 0, sizeof fresh);
ok(cog_grounding_significant(&fresh, &n, F, R) == COG_SIG_FIRST_RECORD,
"no prior version -> FIRST_RECORD");
}
printf("\n== 3. INHERENT MOVES BYPASS THE SALIENCE GATE ==\n");
ok(cog_significance_inherent(COG_SIG_POLARITY_FLIP), "polarity flip is inherent");
ok(cog_significance_inherent(COG_SIG_PROVENANCE_CHANGE), "provenance change is inherent");
ok(cog_significance_inherent(COG_SIG_FIRST_RECORD), "first record is inherent");
ok(!cog_significance_inherent(COG_SIG_FACTUAL_FLOOR), "a floor crossing is NOT inherent");
ok(!cog_significance_inherent(COG_SIG_NONE), "NONE is not inherent");
printf("\n== 4. THE STORED/DERIVED SPLIT: DERIVED VALUES ARE NEVER SERIALIZED ==\n");
{
CogGrounding g = base();
g.decay = 0.3333; g.factual_now = 0.1234; g.relational_now = 0.2345;
g.associative_now = 0.4567; g.age_ms = 999999; g.stale = 1;
char* m = cog_grounding_metadata("pre-existing=keepme", &g);
ok(m != NULL, "serializer returns a document");
ok(m && strstr(m, "pre-existing=keepme"), "pre-existing edge metadata preserved verbatim");
ok(m && strstr(m, "GRD1"), "GRD1 magic present");
ok(m && !strstr(m, "0.3333"), "decay is NOT stored");
ok(m && !strstr(m, "0.1234"), "factual_now is NOT stored");
ok(m && !strstr(m, "0.2345"), "relational_now is NOT stored");
ok(m && !strstr(m, "0.4567"), "associative_now is NOT stored");
ok(m && !strstr(m, "999999"), "age is NOT stored");
ok(m && strstr(m, "told"), "provenance class IS stored");
ok(m && strstr(m, "0.6"), "the factual/relational dimensions ARE stored");
if (m) { printf("\n --- serialized GRD1 block ---\n%s -----------------------------\n", m); }
free(m);
}
printf("\n%s (%d failure%s)\n\n", fails ? "SOME TESTS FAILED" : "ALL TESTS PASSED",
fails, fails == 1 ? "" : "s");
return fails ? 1 : 0;
}
+54 -25
View File
@@ -13,7 +13,7 @@
// relations add edges. Every node enters with PROVENANCE + grounding-level // relations add edges. Every node enters with PROVENANCE + grounding-level
// + stewardship class from the moment of entry. // + stewardship class from the moment of entry.
// //
// transduce_manifold() is THE single mechanism one function, polymorphic, with no // transduce_bytes() is THE single mechanism one function, polymorphic, with no
// content-type branch inside it. It does not ask whether a payload is // content-type branch inside it. It does not ask whether a payload is
// prose, structured data, or raw/opaque bytes (audio, or anything else); // prose, structured data, or raw/opaque bytes (audio, or anything else);
// it runs one boundary-scan-with-fixed-window-fallback chunking algorithm // it runs one boundary-scan-with-fixed-window-fallback chunking algorithm
@@ -401,25 +401,54 @@ fn head80(s: String) -> String {
// truncates at the first embedded NUL, which is routine in real binary // truncates at the first embedded NUL, which is routine in real binary
// bytes) is a MECHANICAL fidelity concern that belongs to whatever produced // bytes) is a MECHANICAL fidelity concern that belongs to whatever produced
// `source` (see ingest_file's file_source_string below) not a // `source` (see ingest_file's file_source_string below) not a
// content-type judgment made in here. transduce_manifold() never learns whether a // content-type judgment made in here. transduce_bytes() never learns whether a
// chunk is plain text or a base64-encoded raw-byte window; every chunk is // chunk is plain text or a base64-encoded raw-byte window; every chunk is
// handled identically either way. // handled identically either way.
// RENAMED transduce -> transduce_manifold (2026-08-16). Two reasons, and the // NAMING, CORRECTED 2026-08-16 (second pass). This function was renamed
// first is not the interesting one: // `transduce` -> `transduce_bytes` earlier the same day, on the reasoning
// that it "was never signal->geometry — it chunks already-extracted content
// and PACKS it into a node+edge manifold, one layer up, and it had taken the
// name that belongs to the primitive underneath it."
// //
// 1. Mechanical: `transduce` is now a LANGUAGE primitive in el_runtime.h // THAT REASONING WAS BACKWARDS, and it is worth recording why rather than
// (transduce(signal, modality) -> Geometry). Every El `fn name(...)` // quietly re-renaming. Producing a node+edge manifold is not a layer above
// compiles to a global C symbol with that exact name, so keeping this // transduction it IS transduction. Transduction is not conversion. When you
// name here is a hard `conflicting types for 'transduce'` compile error // take in music you do not store the song as one discrete geometry; you break
// the moment ingest.c links el_runtime.c. Measured, not anticipated. // it into its component parts and store the geometry of each along with the
// relations between them. The song is the structure of those relations.
// Signal -> one vector is the operation UNDERNEATH transduction, and its name
// is encoding, or geometry. So the layer that was doing it right got renamed
// out of the way so the layer doing it wrong could have the name.
// //
// 2. Actual: this function was never signal->geometry. It chunks already- // The primitive has since been corrected: `transduce(signal, modality)` now
// extracted content and PACKS it into a node+edge manifold a real // returns a Manifold components plus relations not a Geometry
// operation, but one layer up, and it had taken the name that belongs to // (el_runtime.c, "Manifold"). The two layers are therefore doing the SAME KIND
// the primitive underneath it. `transduce` is where a signal becomes // of thing, and the inversion dissolves rather than needing to be re-argued.
// geometry; `transduce_manifold` is where extracted content becomes //
// structure. Nothing about this function's behaviour changed. // What is left is a real distinction, and it is about MODALITY, not layering:
fn transduce_manifold(nodes: [String], edges: [String], source: String, //
// * `transduce(signal, modality)` dispatches to a realizer that KNOWS the
// modality and can name its components for audio: pitch, interval,
// rhythm, harmonic function.
// * `transduce_bytes` below is the OPAQUE-BYTES realizer: the decomposition
// available to a reader that knows nothing about what it is reading. It
// still yields components and relations (chunk nodes; contains / precedes
// / section_of edges), which is why it is transduction and not packing. It
// just cuts on the only structure visible without understanding byte
// boundaries so its components are positional rather than meaningful.
// That is a LIMITATION of this realizer, not the definition of the
// operation.
//
// The name is suffixed by its modality, not demoted to a lesser layer. Keeping
// a distinct symbol is also still mechanically required: every El `fn name`
// compiles to a global C symbol, so reusing `transduce` here is a hard
// `conflicting types` error the moment ingest.c links el_runtime.c.
//
// WHERE THIS SHOULD GO: this function should become a registered realizer
// returning a real Manifold, so ingest rides the same primitive as every other
// modality instead of carrying a parallel implementation. Not done here.
// Nothing about this function's behaviour changed in this pass.
fn transduce_bytes(nodes: [String], edges: [String], source: String,
prov: String, ground: String, steward: String, prov: String, ground: String, steward: String,
root_lid: String, root_title: String) -> [String] { root_lid: String, root_title: String) -> [String] {
let tagbase: String = "prov:" + prov + " ground:" + ground + " steward:" + steward let tagbase: String = "prov:" + prov + " ground:" + ground + " steward:" + steward
@@ -546,8 +575,8 @@ fn default_steward() -> String {
// trustworthy verbatim. When they don't (silent truncation happened), // trustworthy verbatim. When they don't (silent truncation happened),
// rebuild the payload as base64-encoded fixed-size windows read directly // rebuild the payload as base64-encoded fixed-size windows read directly
// off disk (fs_read_b64_chunk binary-safe in C), joined with the same // off disk (fs_read_b64_chunk binary-safe in C), joined with the same
// "\n\n" boundary marker transduce_manifold()'s generic scan already looks for, so // "\n\n" boundary marker transduce_bytes()'s generic scan already looks for, so
// transduce_manifold() sees one ordinary boundary-delimited payload and runs its one // transduce_bytes() sees one ordinary boundary-delimited payload and runs its one
// algorithm on it exactly as it would on prose it never learns that a // algorithm on it exactly as it would on prose it never learns that a
// fidelity problem occurred upstream, let alone why. // fidelity problem occurred upstream, let alone why.
fn file_source_string(path: String, text: String, real_size: Int) -> String { fn file_source_string(path: String, text: String, real_size: Int) -> String {
@@ -556,7 +585,7 @@ fn file_source_string(path: String, text: String, real_size: Int) -> String {
// 3072 raw bytes -> 4096 base64 chars (3 divides evenly into base64's // 3072 raw bytes -> 4096 base64 chars (3 divides evenly into base64's
// 3-byte/4-char ratio); keeps each resulting node's content a clean, // 3-byte/4-char ratio); keeps each resulting node's content a clean,
// bounded, low-kilobytes unit, same order of magnitude as the fixed // bounded, low-kilobytes unit, same order of magnitude as the fixed
// fallback window in transduce_manifold() itself. // fallback window in transduce_bytes() itself.
let win: Int = 3072 let win: Int = 3072
let out: String = "" let out: String = ""
let off: Int = 0 let off: Int = 0
@@ -576,7 +605,7 @@ fn file_source_string(path: String, text: String, real_size: Int) -> String {
} }
// ingest one file -> report JSON. Uniform for every file regardless of // ingest one file -> report JSON. Uniform for every file regardless of
// extension or content transduce_manifold() decides nothing about content-type, so // extension or content transduce_bytes() decides nothing about content-type, so
// neither does this function; it only decides whether the raw bytes made it // neither does this function; it only decides whether the raw bytes made it
// through the read intact (file_source_string), which is a fidelity // through the read intact (file_source_string), which is a fidelity
// question, not a format one. // question, not a format one.
@@ -588,14 +617,14 @@ fn ingest_file(path: String) -> String {
return "{\"error\":\"empty or unreadable\",\"path\":" + j_q(path) + "}" return "{\"error\":\"empty or unreadable\",\"path\":" + j_q(path) + "}"
} }
let prov: String = "file:" + path let prov: String = "file:" + path
let packed: [String] = transduce_manifold(el_list_empty(), el_list_empty(), let packed: [String] = transduce_bytes(el_list_empty(), el_list_empty(),
source, prov, default_ground(), default_steward(), source, prov, default_ground(), default_steward(),
"doc:" + basename(path), basename(path)) "doc:" + basename(path), basename(path))
return merge_packed(packed) return merge_packed(packed)
} }
// ingest a directory: walk one level, ingest every file found, aggregate. // ingest a directory: walk one level, ingest every file found, aggregate.
// No extension filter transduce_manifold() handles any payload uniformly now, so // No extension filter transduce_bytes() handles any payload uniformly now, so
// there is no content-type gate at the directory boundary either. // there is no content-type gate at the directory boundary either.
fn ingest_dir(path: String) -> String { fn ingest_dir(path: String) -> String {
let entries: [String] = fs_list(path) let entries: [String] = fs_list(path)
@@ -630,7 +659,7 @@ fn ingest_dir(path: String) -> String {
fn ingest_url(url: String) -> String { fn ingest_url(url: String) -> String {
let body: String = http_get(url) let body: String = http_get(url)
if str_eq(body, "") { return "{\"error\":\"empty fetch\",\"url\":" + j_q(url) + "}" } if str_eq(body, "") { return "{\"error\":\"empty fetch\",\"url\":" + j_q(url) + "}" }
let packed: [String] = transduce_manifold(el_list_empty(), el_list_empty(), let packed: [String] = transduce_bytes(el_list_empty(), el_list_empty(),
body, "url:" + url, "extracted", "public-web", body, "url:" + url, "extracted", "public-web",
"url:" + url, url) "url:" + url, url)
return merge_packed(packed) return merge_packed(packed)
@@ -645,7 +674,7 @@ fn ingest_llm(query: String) -> String {
let resp: String = http_post_json("http://127.0.0.1:11434/api/generate", body) let resp: String = http_post_json("http://127.0.0.1:11434/api/generate", body)
let answer: String = json_get_string(resp, "response") let answer: String = json_get_string(resp, "response")
if str_eq(answer, "") { return "{\"error\":\"no model response\"}" } if str_eq(answer, "") { return "{\"error\":\"no model response\"}" }
let packed: [String] = transduce_manifold(el_list_empty(), el_list_empty(), let packed: [String] = transduce_bytes(el_list_empty(), el_list_empty(),
answer, "llm:" + model + ":" + query, "candidate-provisional", "guide-provisional", answer, "llm:" + model + ":" + query, "candidate-provisional", "guide-provisional",
"llm:" + query, "guide answer: " + query) "llm:" + query, "guide answer: " + query)
return merge_packed(packed) return merge_packed(packed)
@@ -697,7 +726,7 @@ fn ingest_stream(path: String) -> String {
// It is NOT a content-type flag: it says nothing about what's inside the // It is NOT a content-type flag: it says nothing about what's inside the
// bytes once fetched, and none of the five ingest_* functions it selects // bytes once fetched, and none of the five ingest_* functions it selects
// among interpret their payload differently by content shape anymore // among interpret their payload differently by content shape anymore
// they all hand off to the single, format-agnostic transduce_manifold(). The old // they all hand off to the single, format-agnostic transduce_bytes(). The old
// "structured" value (a caller-declared alias for "file", used only to hint // "structured" value (a caller-declared alias for "file", used only to hint
// the now-removed JSON-vs-prose branch) is gone along with that branch. // the now-removed JSON-vs-prose branch) is gone along with that branch.
let kind: String = env("INGEST_KIND") let kind: String = env("INGEST_KIND")
+11
View File
@@ -73,6 +73,17 @@ When you add a C builtin (verbatim-emit recipe — the El name is emitted as the
2. Add a `__`-prefixed thin wrapper in `el_seed.c` and declare it in `el_seed.h`. 2. Add a `__`-prefixed thin wrapper in `el_seed.c` and declare it in `el_seed.h`.
3. Add the name to `builtin_arity` in `el-compiler/src/codegen.el` — add **both** the plain and `__`-prefixed spellings. 3. Add the name to `builtin_arity` in `el-compiler/src/codegen.el` — add **both** the plain and `__`-prefixed spellings.
4. Rebuild the elc binary (see below) and confirm the self-host fixpoint is byte-identical. 4. Rebuild the elc binary (see below) and confirm the self-host fixpoint is byte-identical.
5. **Prove it with a NEGATIVE CONTROL.** Show the test FAILING on a build without your change, then passing with it. A test that has never been seen to fail has proven nothing.
> **Step 5 is not optional, and step 4 does not cover it.** The fixpoint proves the *compiler reproduces itself*. It says nothing whatsoever about whether your builtin works. A recipe ending at "byte-identical" reads as complete while having verified nothing about the thing just added — which is why this file, until 2026-08-16, produced builtins with no tests at all.
>
> Measured cost of the omission (2026-08-16): `engram_node_set_emb`, `engram_curiosity_json` and `dream_set_handler` were all added in one session with zero tests. Separately, a UTF-8 fix was written, tested, and **the test passed on the unpatched build too** — the defect was elsewhere entirely, and only building the pre-fix binary exposed it. Without a negative control that fix would have merged as verified.
>
> Two shapes that pass while proving nothing, both hit the same day:
> - A test that never exercises your change (the route supplied a default that bypassed the code under test).
> - An induction that loses a race. `curl --max-time` on a large response left *both* builds alive; only `SO_LINGER 0` — a genuine RST, so the peer is provably gone — reproduced the failure. Six of ten attempts is not a control.
>
> Before every probe, confirm **your** process bound the port (`lsof -nP -iTCP:<port>`, match the PID). A stale instance answering on the port has silently produced false results here more than once, and `pkill -f` does not reliably match an argv like `./engram`.
Worked example: the `engram_assert_json` (op_assert seam) and `engram_node_full_in`/`engram_connect_in` (purview write-side) primitives added 2026-08-15 follow exactly this recipe. Worked example: the `engram_assert_json` (op_assert seam) and `engram_node_full_in`/`engram_connect_in` (purview write-side) primitives added 2026-08-15 follow exactly this recipe.
+183 -168
View File
@@ -1,67 +1,33 @@
// transduce.el geometry as a first-class El value, and a realizer written // transduce.el transduction decomposes a signal into components and the
// in El. Runnable: this is the worked example for the transduce surface, and // relations between them. Runnable: this is the worked example for the
// it doubles as an executable proof because it checks every claim it makes. // transduce surface, and it exits non-zero if any claim in it stops being true.
// //
// elc lang/examples/transduce.el > transduce.c // elc lang/examples/transduce.el > transduce.c
// cc -std=c11 -O2 -I lang/runtime -o transduce transduce.c \ // cc -std=c11 -O2 -I lang/runtime -o transduce transduce.c \
// lang/runtime/el_runtime.c lang/runtime/el_seed.c \ // lang/runtime/el_runtime.c lang/runtime/el_seed.c \
// lang/runtime/engram_*.c -lcurl -lpthread -lm // lang/runtime/engram_store.c lang/runtime/engram_vindex.c \
// lang/runtime/engram_cognition.c lang/runtime/engram_geometry.c \
// lang/runtime/engram_reason.c lang/runtime/engram_verify.c \
// -lcurl -lpthread -lm
// ./transduce # exits 0 only if every check passes // ./transduce # exits 0 only if every check passes
// //
// (A `test "..."` form of the same checks lives in // It writes to an IN-MEMORY engram (leave ENGRAM_STORE unset) and contacts no
// lang/tests/native/test_transduce.el, for when the native harness is // server. The same claims are asserted by the native harness in
// repaired the shipped elc currently emits calls to __el_reg_count and // lang/tests/native/test_transduce.el.
// friends without emitting their definitions, which breaks every native test
// equally, test_math.el included. Verified 2026-08-16, unrelated to this work.)
// //
// WHY THIS EXISTS. Until 2026-08-16 no El ingest path could carry a vector: // WHAT CHANGED, AND WHY IT MATTERS. #144 shipped
// nodes took text, and geometry was DERIVED from that text. Text was the // `transduce(signal, modality) -> Geometry`: one vector per signal. That made
// mandatory entry medium, so any non-text modality had to be DESCRIBED in // transduction a CONVERSION take a thing, encode it, store a position and
// prose first and the geometry we reasoned over was the geometry OF THE // what a conversion returns is a fingerprint. A fingerprint can be matched and
// DESCRIPTION, not of the signal. Two things fix that, and both are shown // ranked, and that is all it can ever do. It cannot be decomposed, cannot have
// below: geometry is a VALUE that carries its own width, and a REALIZER is an // one part grounded while another is not, and cannot be contradicted in one
// ordinary El function so admitting a new modality never requires a runtime // part while holding in another, because it has no parts.
// patch.
// //
// COMPARISON DISCIPLINE (measured, not stylistic): elc lowers `a == b` // A song is not a point. It decomposes into pitch, interval, rhythm, harmonic
// numerically only when both operand NAMES are in the per-function int-name // function components, each with its own geometry, plus the relations among
// set that `let x: Int` populates. A bare `f(x) == 0` is not a registered // them. THE SONG IS THE STRUCTURE OF THE RELATIONS. transduce now returns a
// name and lowers to str_eq strcmp on two integers as pointers. `<` and `>` // Manifold, and a realizer's job is to say what its modality's components ARE.
// lower directly with no inference, so truthiness is written `> 0` / `< 1`.
// A realizer, written entirely in El
// Not in the runtime. Not known to the compiler. Registered by NAME and
// dispatched to through transduce(). That is the whole claim.
fn tone_realizer(signal: String) -> Geometry {
let g: Geometry = geometry_new(4)
let n: Int = str_len(signal)
let a: Int = geometry_set(g, 0, int_to_float(n))
let b: Int = geometry_set(g, 1, int_to_float(n * 2))
let c: Int = geometry_set(g, 2, int_to_float(n * 3))
let d: Int = geometry_set(g, 3, int_to_float(n * 4))
g
}
// A second modality, to show the registry keys on modality rather than just
// returning whatever was registered last.
fn pulse_realizer(signal: String) -> Geometry {
let g: Geometry = geometry_new(2)
let a: Int = geometry_set(g, 0, 1.0)
let b: Int = geometry_set(g, 1, 0.0)
g
}
// A deliberately BROKEN realizer: returns something that is not a Geometry.
fn bogus_realizer(signal: String) -> Geometry {
return 12345
}
// Fails FAST rather than accumulating a count, for a measured reason: a first
// cut wrote `let fails: Int = fails + check(...)` and `+` lowered to STRING
// CONCAT, because elc dispatches `+` on whether both operands are known-Int and
// a user-defined fn call is not so the counter printed 4343632752, a pointer.
// Nothing was wrong with the checks; the tally was lying. Exiting at the first
// failure needs no arithmetic at all, so there is nothing left to get wrong.
fn check(ok: Int, label: String) -> Int { fn check(ok: Int, label: String) -> Int {
if ok > 0 { if ok > 0 {
println(" ok " + label) println(" ok " + label)
@@ -84,128 +50,177 @@ fn eq_int(a: Int, b: Int) -> Int {
return 0 return 0
} }
// A DECOMPOSING realizer, written entirely in El
// "tone" signals are note letters, e.g. "CEG". This does NOT return one vector
// for the chord. It returns the PARTS one component per note, one per
// interval between adjacent notes and the relations that make those parts a
// chord rather than an unordered bag of pitches.
//
// The interval is deliberately a COMPONENT, not a field on a note. An interval
// is a thing with its own geometry belonging to neither endpoint; modelling it
// as an attribute of one of them is the same collapse, one level down.
fn tone_realizer(signal: String) -> Manifold {
let m: Manifold = manifold_new()
let n: Int = str_len(signal)
let i: Int = 0
while i < n {
let code: Int = str_char_code(signal, i)
let g: Geometry = geometry_new(2)
let s0: Int = geometry_set(g, 0, int_to_float(code))
let s1: Int = geometry_set(g, 1, int_to_float(i))
let idx: Int = manifold_add(m, "note:" + int_to_str(i), "pitch", g)
let f: Int = geometry_free(g)
i = i + 1
}
let j: Int = 1
while j < n {
let a: Int = str_char_code(signal, j - 1)
let b: Int = str_char_code(signal, j)
let lo: String = "note:" + int_to_str(j - 1)
let hi: String = "note:" + int_to_str(j)
let key: String = "interval:" + int_to_str(j - 1) + "-" + int_to_str(j)
let g: Geometry = geometry_new(1)
let s: Int = geometry_set(g, 0, int_to_float(b - a))
let idx: Int = manifold_add(m, key, "interval", g)
let f: Int = geometry_free(g)
let e1: Int = manifold_relate(m, key, "spans", lo, 0.9)
let e2: Int = manifold_relate(m, key, "spans", hi, 0.9)
let e3: Int = manifold_relate(m, lo, "sounds_before", hi, 0.8)
j = j + 1
}
m
}
// #144's contract, kept as a control: one vector for the whole signal.
fn fingerprint_realizer(signal: String) -> Geometry {
let g: Geometry = geometry_new(4)
let n: Int = str_len(signal)
let a: Int = geometry_set(g, 0, int_to_float(n))
g
}
fn main() -> Void { fn main() -> Void {
println("geometry is a value that carries its own width")
let g8: Geometry = geometry_new(8)
let _c: Int = check(geometry_is(g8), "geometry_new returns a live Geometry")
let d8: Int = geometry_dim(g8)
let _c: Int = check(eq_int(d8, 8), "a Geometry carries its own width (8)")
let _c: Int = check(geometry_free(g8), "geometry_free reports what it did")
println("nonsense is refused — with no arbitrary max-dim bound")
// #141 needed `dim <= 8192` only to bound an allocation sized from a
// caller's CLAIM about a string's length. A value that carries its own
// width has nothing left to validate.
let z: Geometry = geometry_new(0)
let zi: Int = geometry_is(z)
let _c: Int = check(1 - zi, "dim 0 is not a geometry")
let ng: Geometry = geometry_new(-4)
let ngi: Int = geometry_is(ng)
let _c: Int = check(1 - ngi, "negative dim is not a geometry")
let nd: Int = geometry_dim(0)
let _c: Int = check(1 - nd, "geometry_dim of a non-geometry is 0, not a crash")
let nf: Int = geometry_free(0)
let _c: Int = check(1 - nf, "geometry_free of a non-geometry is a no-op")
println("components round-trip, and out-of-range is refused")
let g3: Geometry = geometry_new(3)
let s0: Int = geometry_set(g3, 0, 1.5)
let s1: Int = geometry_set(g3, 1, -2.5)
let _c: Int = check(s0, "set in range succeeds")
let oob: Int = geometry_set(g3, 3, 9.0)
let _c: Int = check(1 - oob, "set out of range is refused, not silently dropped")
let _c: Int = check(near(geometry_get(g3, 0), 1.5), "component 0 round-trips")
let _c: Int = check(near(geometry_get(g3, 1), -2.5), "component 1 round-trips (negative)")
let ff3: Int = geometry_free(g3)
println("hex is an EDGE adapter, and derives its own width")
// little-endian float32: 1.0 = 0000803f, 2.0 = 00000040
let gh: Geometry = geometry_from_f32le_hex("0000803f00000040")
let _c: Int = check(geometry_is(gh), "valid hex decodes to a Geometry")
let dh: Int = geometry_dim(gh)
let _c: Int = check(eq_int(dh, 2), "width DERIVED from input, never supplied")
let _c: Int = check(near(geometry_get(gh, 0), 1.0), "first component decoded")
let _c: Int = check(near(geometry_get(gh, 1), 2.0), "second component decoded")
let back: String = geometry_to_f32le_hex(gh)
let _c: Int = check(str_eq(back, "0000803f00000040"), "hex round-trips exactly")
let ffh: Int = geometry_free(gh)
println("malformed hex is refused")
let he: Geometry = geometry_from_f32le_hex("")
let hei: Int = geometry_is(he)
let _c: Int = check(1 - hei, "empty hex is not a geometry")
let hr: Geometry = geometry_from_f32le_hex("0000803f0000")
let hri: Int = geometry_is(hr)
let _c: Int = check(1 - hri, "length not a multiple of 8 is refused")
let hn: Geometry = geometry_from_f32le_hex("zzzzzzzz")
let hni: Int = geometry_is(hn)
let _c: Int = check(1 - hni, "non-hex characters are refused")
println("a realizer declared in El is a first-class realizer") println("a realizer declared in El is a first-class realizer")
let reg: Int = realizer_register("tone", "tone_realizer") let reg: Int = realizer_register("tone", "tone_realizer")
let _c: Int = check(reg, "an El fn registers as a realizer BY NAME") let _c: Int = check(reg, "an El fn registers as a realizer by name")
let _c: Int = check(realizer_has("tone"), "the modality now has an organ") let _c: Int = check(realizer_has("tone"), "the modality now has an organ")
let gt: Geometry = transduce("aaa", "tone")
let _c: Int = check(geometry_is(gt), "transduce returns real geometry")
let dt: Int = geometry_dim(gt)
let _c: Int = check(eq_int(dt, 4), "the El realizer determined the width, not the runtime")
// str_len("aaa") == 3, so component 0 must be 3.0 proof the signal
// actually reached the El function rather than a stub answering for it.
let _c: Int = check(near(geometry_get(gt, 0), 3.0), "the signal REACHED the El realizer")
let fft: Int = geometry_free(gt)
println("distinct signals transduce to distinct geometry") println("transduction decomposes a signal into parts")
let g1: Geometry = transduce("aa", "tone") let m: Manifold = transduce("CEG", "tone")
let g2: Geometry = transduce("aaaaa", "tone") let _c: Int = check(manifold_is(m), "transduce returns a real Manifold")
let a1: Float = geometry_get(g1, 0) let sz: Int = manifold_size(m)
let a2: Float = geometry_get(g2, 0) let _c: Int = check(eq_int(sz, 5), "three notes and two intervals are five parts")
// 5 - 2 = 3. If transduction were a stub these would be equal. let rc: Int = manifold_rel_count(m)
let _c: Int = check(near(a2 - a1, 3.0), "different signals produce different geometry") let _c: Int = check(eq_int(rc, 6), "and they stand in six stated relations")
let ff1: Int = geometry_free(g1)
let ff2: Int = geometry_free(g2)
println("the registry keys on modality") println("every part is addressable BY KEY, which is what survives persistence")
let r2: Int = realizer_register("pulse", "pulse_realizer") let i_c: Int = manifold_index_of(m, "note:0")
let _c: Int = check(r2, "a second modality registers independently") let _c: Int = check(1 - eq_int(i_c, -1), "the first note is addressable on its own")
let mt: Geometry = transduce("aaa", "tone") let i_iv: Int = manifold_index_of(m, "interval:0-1")
let mp: Geometry = transduce("aaa", "pulse") let _c: Int = check(1 - eq_int(i_iv, -1), "so is the interval between the first two")
let mdt: Int = geometry_dim(mt) let miss: Int = manifold_index_of(m, "never_added")
let mdp: Int = geometry_dim(mp) let _c: Int = check(eq_int(miss, -1), "an unknown key is -1, not component 0")
let _c: Int = check(eq_int(mdt, 4), "tone still routes to its own realizer")
let _c: Int = check(eq_int(mdp, 2), "pulse routes to a different realizer")
let ffm1: Int = geometry_free(mt)
let ffm2: Int = geometry_free(mp)
println("no organ is reported as no organ") println("parts carry their own geometry, and may differ in width")
// A modality with no realizer must transduce to NOTHING. It must never let gn: Geometry = manifold_geometry(m, i_c)
// fall back to embedding a description of the signal and calling that let _c: Int = check(eq_int(geometry_dim(gn), 2), "a note component is 2 wide")
// perception that silent substitution is the defect this all exists to end. let _c: Int = check(near(geometry_get(gn, 0), 67.0), "and it is C — the signal reached the realizer")
let eh: Int = realizer_has("echolocation") let gi: Geometry = manifold_geometry(m, i_iv)
let _c: Int = check(1 - eh, "unregistered modality has no organ") let _c: Int = check(eq_int(geometry_dim(gi), 1), "an interval component is 1 wide")
let ge: Geometry = transduce("anything", "echolocation") // A single vector per signal cannot represent parts of unequal width at all.
let gei: Int = geometry_is(ge) let _c: Int = check(near(geometry_get(gi, 0), 2.0), "C to E is two semitones")
let _c: Int = check(1 - gei, "no realizer means NO geometry, not fake geometry") let f1: Int = geometry_free(gn)
let f2: Int = geometry_free(gi)
println("an unresolvable realizer name fails at WIRING time") println("the relations are content no single part carries")
let bad: Int = realizer_register("ghost", "no_such_function_anywhere") // That "2" above is not a property of C and not a property of E. It exists
let _c: Int = check(1 - bad, "unresolvable realizer name is a registration failure") // only BETWEEN them, so a representation with no relations cannot hold it.
let gh2: Int = realizer_has("ghost") let spans: Int = 0
let _c: Int = check(1 - gh2, "and nothing gets registered") let k: Int = 0
while k < rc {
if str_eq(manifold_rel_name(m, k), "spans") {
if str_eq(manifold_rel_from(m, k), "interval:0-1") { spans = spans + 1 }
}
k = k + 1
}
let _c: Int = check(eq_int(spans, 2), "the interval is wired to both notes it spans")
println("a realizer returning non-geometry transduces nothing") println("relation weight IS the grounding (correspondence-and-censorship §1)")
let rb: Int = realizer_register("bogus", "bogus_realizer") let wk: Int = 0
let _c: Int = check(rb, "the symbol resolves, so registration succeeds") let found: Int = 0
let gb: Geometry = transduce("x", "bogus") while wk < rc {
let gbi: Int = geometry_is(gb) if str_eq(manifold_rel_name(m, wk), "sounds_before") {
let _c: Int = check(1 - gbi, "contract enforced at the boundary: nothing handed back") if near(manifold_rel_weight(m, wk), 0.8) > 0 { found = 1 }
}
wk = wk + 1
}
let _c: Int = check(found, "the ordering relation carries the weight its realizer stated")
println("norm lets a caller check a realizer emitted signal, not zeros") println("the decomposition persists as real, separately addressable nodes")
let gn: Geometry = geometry_new(2) let ids: [String] = el_list_empty()
let _c: Int = check(near(geometry_norm(gn), 0.0), "a fresh geometry is zero — norm says so") let n0: Int = engram_node_count()
let n0: Int = geometry_set(gn, 0, 3.0) let e0: Int = engram_edge_count()
let n1: Int = geometry_set(gn, 1, 4.0) let pi: Int = 0
let _c: Int = check(near(geometry_norm(gn), 5.0), "3-4-5: norm is 5") while pi < sz {
let ffn: Int = geometry_free(gn) let key: String = manifold_key(m, pi)
let g: Geometry = manifold_geometry(m, pi)
let id: String = engram_node("component " + key, "Concept", 0.6)
let att: Int = node_attach_geometry(id, g)
ids = el_list_append(ids, id)
let ff: Int = geometry_free(g)
pi = pi + 1
}
let ri: Int = 0
while ri < rc {
let fi: Int = manifold_index_of(m, manifold_rel_from(m, ri))
let ti: Int = manifold_index_of(m, manifold_rel_to(m, ri))
engram_connect(el_list_get(ids, fi), el_list_get(ids, ti),
manifold_rel_weight(m, ri), manifold_rel_name(m, ri))
ri = ri + 1
}
let _c: Int = check(eq_int(engram_node_count() - n0, 5), "one signal became five nodes")
let _c: Int = check(eq_int(engram_edge_count() - e0, 6), "and six edges between them")
println("each part's geometry is independently readable back off its node")
let id_c: String = el_list_get(ids, manifold_index_of(m, "note:0"))
let id_iv: String = el_list_get(ids, manifold_index_of(m, "interval:0-1"))
let _c: Int = check(eq_int(node_geometry_dim(id_c), 2), "note:0 node carries a 2-wide geometry")
let _c: Int = check(eq_int(node_geometry_dim(id_iv), 1), "interval:0-1 node carries a 1-wide one")
println("one part can be grounded without touching its siblings")
let ear: String = engram_node("evidence: heard a C in the recording", "Memory", 0.7)
engram_connect(ear, id_c, 0.95, "corroborates")
let _c: Int = check(engram_edge_between(ear, id_c), "evidence attaches to note:0 specifically")
let id_g: String = el_list_get(ids, manifold_index_of(m, "note:2"))
let _c: Int = check(1 - engram_edge_between(ear, id_g), "and NOT to note:2 — the sibling is untouched")
// This is the whole gain, and it is impossible with a fingerprint: with one
// node per signal, "the C is corroborated" and "the G is not" have the same
// grounding target and cannot both be recorded.
let _c: Int = check(eq_int(node_geometry_dim(id_g), 2), "note:2 geometry is intact regardless")
println("a fingerprint realizer transduces NOTHING")
// #144's contract exactly: signal in, one Geometry out. It resolves, so the
// organ is present but it does not decompose, so it does not transduce.
// "No organ" and "an organ that only fingerprints" must not look alike.
let rf: Int = realizer_register("fingerprint", "fingerprint_realizer")
let _c: Int = check(rf, "the symbol resolves, so registration succeeds")
let mf: Manifold = transduce("x", "fingerprint")
let _c: Int = check(1 - manifold_is(mf), "a single vector is not a transduction")
println("the one-part case is a size-one manifold, not a bare vector")
let g1: Geometry = geometry_new(3)
let s1: Int = geometry_set(g1, 0, 5.0)
let ms: Manifold = manifold_single("level", "scalar", g1)
let _c: Int = check(manifold_is(ms), "manifold_single yields a real Manifold")
let _c: Int = check(eq_int(manifold_size(ms), 1), "of size one — visibly degenerate, not hidden")
let fg: Int = geometry_free(g1)
let fs: Int = manifold_free(ms)
println("no organ is still reported as no organ")
let me: Manifold = transduce("anything", "echolocation")
let _c: Int = check(1 - manifold_is(me), "no realizer means no manifold, not a fake one")
let fm: Int = manifold_free(m)
// Reaching here means nothing called exit(1) along the way. // Reaching here means nothing called exit(1) along the way.
println("") println("")
+883 -108
View File
File diff suppressed because it is too large Load Diff
+66 -11
View File
@@ -625,20 +625,70 @@ el_val_t geometry_free(el_val_t g); /* 1 if freed, 0 if not a
el_val_t geometry_from_f32le_hex(el_val_t hex); /* 0 on empty/odd-length/non-hex */ el_val_t geometry_from_f32le_hex(el_val_t hex); /* 0 on empty/odd-length/non-hex */
el_val_t geometry_to_f32le_hex(el_val_t g); /* "" if not a Geometry */ el_val_t geometry_to_f32le_hex(el_val_t g); /* "" if not a Geometry */
/* ── Realizers + transduce ─────────────────────────────────────────────────── /* ── Manifold: the result of a transduction ──────────────────────────────────
* A REALIZER maps one modality into geometry. Registration is by NAME, so a * A transduced signal is a SUBGRAPH — named components, each with its own
* new modality never requires a runtime patch: every El `fn name(...)` * geometry, plus typed weighted relations among them — not a single vector.
* compiles to a global C symbol with that exact name, and the registry * One vector is a fingerprint: matchable, rankable, and nothing else. A song
* resolves it with dlsym against the running binary — the same mechanism * decomposes into pitch, interval, rhythm, harmonic function; the song IS the
* http_set_handler already relies on. * structure of those relations, and collapsing it to a point discards exactly
* what made it reasonable-about. See el_runtime.c ("Manifold") for the full
* rationale, the key-addressing rule, and the ownership contract.
* *
* fn tone_realizer(signal: String) -> Geometry { ... } * Components are addressed BY KEY, never by index, because the key is what
* survives persistence: a component becomes a node, and it is separately
* groundable precisely because it is separately named. Relation weight IS the
* grounding (correspondence-and-censorship.md §1) — one quantity, no separate
* score, nothing computed on read.
*
* OWNERSHIP: a Manifold is owned by the El caller and released with
* manifold_free, which also releases every component's geometry. manifold_add
* COPIES the geometry it is given and manifold_geometry RETURNS a copy, so no
* component's vector is ever aliased in either direction. */
el_val_t manifold_new(void); /* empty; 0 on failure */
el_val_t manifold_is(el_val_t m); /* 1 if a live Manifold */
el_val_t manifold_add(el_val_t m, el_val_t key, el_val_t role, el_val_t g);
/* component index, or -1 on empty/duplicate
* key or a value that is not a Geometry */
el_val_t manifold_relate(el_val_t m, el_val_t from, el_val_t rel,
el_val_t to, el_val_t weight);
/* 1 ok / 0 if either endpoint is unknown —
* an unresolvable edge is REFUSED, never
* silently dropped */
el_val_t manifold_size(el_val_t m); /* component count */
el_val_t manifold_rel_count(el_val_t m); /* relation count */
el_val_t manifold_index_of(el_val_t m, el_val_t key); /* index by key, or -1 */
el_val_t manifold_key(el_val_t m, el_val_t i); /* "" if out of range */
el_val_t manifold_role(el_val_t m, el_val_t i); /* "" if out of range */
el_val_t manifold_geometry(el_val_t m, el_val_t i); /* a COPY the caller frees */
el_val_t manifold_rel_from(el_val_t m, el_val_t j); /* source component key */
el_val_t manifold_rel_name(el_val_t m, el_val_t j); /* relation name */
el_val_t manifold_rel_to(el_val_t m, el_val_t j); /* target component key */
el_val_t manifold_rel_weight(el_val_t m, el_val_t j); /* Float — the grounding */
el_val_t manifold_single(el_val_t key, el_val_t role, el_val_t g);
/* the degenerate one-part case, expressible
* but visibly a size-1 manifold rather than
* a parallel path back to a bare vector */
el_val_t manifold_free(el_val_t m); /* 1 if freed, 0 otherwise */
/* ── Realizers + transduce ───────────────────────────────────────────────────
* A REALIZER DECOMPOSES one modality into components and relations. It does
* not encode a signal to a point; that operation is one layer below and is
* called geometry. Registration is by NAME, so a new modality never requires a
* runtime patch: every El `fn name(...)` compiles to a global C symbol with
* that exact name, and the registry resolves it with dlsym against the running
* binary — the same mechanism http_set_handler already relies on.
*
* fn tone_realizer(signal: String) -> Manifold { ... }
* realizer_register("tone", "tone_realizer") * realizer_register("tone", "tone_realizer")
* let g: Geometry = transduce(sample, "tone") * let m: Manifold = transduce(sample, "tone")
*/ *
* SUPERSEDES #144's `transduce -> Geometry`. A realizer that still returns a
* bare Geometry now transduces NOTHING (transduce returns 0), deliberately: an
* organ that only fingerprints must not be indistinguishable from a working
* one. A modality with genuinely one part says so with manifold_single. */
el_val_t realizer_register(el_val_t modality, el_val_t fn_name); /* 1 ok / 0 unresolved */ el_val_t realizer_register(el_val_t modality, el_val_t fn_name); /* 1 ok / 0 unresolved */
el_val_t realizer_has(el_val_t modality); /* 1 if a realizer is registered */ el_val_t realizer_has(el_val_t modality); /* 1 if a realizer is registered */
el_val_t transduce(el_val_t signal, el_val_t modality); /* Geometry, or 0 if no organ */ el_val_t transduce(el_val_t signal, el_val_t modality); /* Manifold, or 0 if no organ */
/* ── Engram local graph primitives ─────────────────────────────────────────── /* ── Engram local graph primitives ───────────────────────────────────────────
* Operate on the CGI's local Engram knowledge graph. * Operate on the CGI's local Engram knowledge graph.
@@ -732,8 +782,13 @@ el_val_t engram_geo_analogy_json(el_val_t a_seeds, el_val_t b_seeds);
el_val_t engram_reason_analogy_json(el_val_t a_seeds, el_val_t b_seeds, el_val_t c_seeds); el_val_t engram_reason_analogy_json(el_val_t a_seeds, el_val_t b_seeds, el_val_t c_seeds);
/* COGNITION (2026-08-14): THE ONE OPERATION + grounding, surfaced live. */ /* COGNITION (2026-08-14): THE ONE OPERATION + grounding, surfaced live. */
el_val_t engram_think_json(el_val_t seeds, el_val_t faculty); el_val_t engram_think_json(el_val_t seeds, el_val_t faculty);
/* GROUNDING (2026-08-16): grounding is an attribute of the RELATION and it IS the
* hebbian weight. ground reads; ground_record writes; trajectory reads the chain. */
el_val_t engram_ground_json(el_val_t claim, el_val_t evidence, el_val_t for_whom); el_val_t engram_ground_json(el_val_t claim, el_val_t evidence, el_val_t for_whom);
el_val_t engram_assert_json(el_val_t claim_id, el_val_t for_whom, el_val_t floor); el_val_t engram_ground_record_json(el_val_t claim, el_val_t evidence,
el_val_t provenance, el_val_t floor);
el_val_t engram_ground_trajectory_json(el_val_t claim, el_val_t evidence);
el_val_t engram_assert_json(el_val_t claim_id, el_val_t for_whom, el_val_t floor, el_val_t rel_floor);
el_val_t engram_attend_json(el_val_t node_id, el_val_t observer, el_val_t salience); el_val_t engram_attend_json(el_val_t node_id, el_val_t observer, el_val_t salience);
el_val_t engram_correspondence_beat_json(el_val_t seeds, el_val_t faculty, el_val_t keystone); el_val_t engram_correspondence_beat_json(el_val_t seeds, el_val_t faculty, el_val_t keystone);
el_val_t engram_consolidate_permanence(el_val_t node_id); el_val_t engram_consolidate_permanence(el_val_t node_id);
+372 -29
View File
@@ -246,14 +246,6 @@ static int put_edge(EngramPagedStore* s, const char* id, const char* from, const
e.metadata = (char*)meta; e.metadata = (char*)meta;
return store_put_edge(s, &e); return store_put_edge(s, &e);
} }
int cog_ground_edge(EngramPagedStore* s, const char* claim_id,
const char* evidence_id, double grounding, const char* for_whom) {
if (!s || !claim_id || !evidence_id) return -1;
char id[512], meta[256];
snprintf(id, sizeof id, "gb-%s-%s-%s", claim_id, evidence_id, for_whom ? for_whom : "global");
snprintf(meta, sizeof meta, "for_whom=%s", for_whom ? for_whom : "-");
return put_edge(s, id, claim_id, evidence_id, COG_GROUNDED_BY_RELATION, grounding, meta);
}
int cog_salient_edge(EngramPagedStore* s, const char* node_id, int cog_salient_edge(EngramPagedStore* s, const char* node_id,
const char* observer_id, double salience) { const char* observer_id, double salience) {
if (!s || !node_id || !observer_id) return -1; if (!s || !node_id || !observer_id) return -1;
@@ -261,35 +253,386 @@ int cog_salient_edge(EngramPagedStore* s, const char* node_id,
snprintf(id, sizeof id, "st-%s-%s", node_id, observer_id); snprintf(id, sizeof id, "st-%s-%s", node_id, observer_id);
return put_edge(s, id, node_id, observer_id, COG_SALIENT_TO_RELATION, salience, NULL); return put_edge(s, id, node_id, observer_id, COG_SALIENT_TO_RELATION, salience, NULL);
} }
int cog_assert_gate(EngramPagedStore* s, const char* claim_id, /* ═══════════════════════════════════════════════════════════════════════════
const char* for_whom, double floor) { * §7 GROUNDING IS THE EDGE'S WEIGHT, AND THE WEIGHT IS A VECTOR.
if (!s || !claim_id) return -1; * See engram_cognition.h §7 for the model and for the measurements the two
if (!(floor > 0)) floor = 0.5; * design decisions (thirteen regions, min aggregate) rest on.
StoreEdge* edges = NULL; size_t n = 0; * ═══════════════════════════════════════════════════════════════════════════ */
if (store_get_edges_from(s, claim_id, &edges, &n) < 0) return -1;
double best = 0.0; int found = 0; /* ── The one decay model. Moved here verbatim from el_runtime.c's
for (size_t i = 0; i < n; i++) { * engram_temporal_decay so nodes and edges share a single implementation and a
if (!edges[i].relation || strcmp(edges[i].relation, COG_GROUNDED_BY_RELATION) != 0) continue; * single set of constants; engram_temporal_decay now delegates. Bit-identical
/* grounded-for-whom: match observer if requested; global (for_whom=-) always counts */ * for nodes: reinforcements := activation_count, lambda_override :=
int match = 1; * temporal_decay_rate.
if (for_whom && edges[i].metadata) { *
const char* fw = strstr(edges[i].metadata, "for_whom="); * This is what makes decay ANALYTIC rather than sampled: between two recorded
if (fw) { fw += 9; if (strcmp(fw, for_whom) != 0 && strcmp(fw, "-") != 0) match = 0; } * versions the trajectory is not unknown, it is known in closed form from the
} * last point and elapsed time. Store the point, read the curve. */
if (match) { found = 1; if (edges[i].weight > best) best = edges[i].weight; } double cog_decay_factor(int64_t age_ms, double reinforcements, double lambda_override) {
} if (age_ms <= 0) return 1.0;
store_edges_free(edges, n); double lambda = (lambda_override > 0.0) ? lambda_override : COG_DECAY_LAMBDA;
if (!found) return 0; /* ungrounded => refuse assertion (still held) */ double age_hours = (double)age_ms / 3600000.0;
return (best >= floor) ? 1 : 0; if (reinforcements < 0) reinforcements = 0;
double t_half = COG_T_HALF_HOURS * (1.0 + log(1.0 + reinforcements));
double factor = exp(-lambda * age_hours / t_half);
if (factor < COG_DECAY_FLOOR) factor = COG_DECAY_FLOOR;
return factor;
} }
const char* cog_prov_name(CogProvClass p) {
switch (p) {
case COG_PROV_OBSERVED: return "observed";
case COG_PROV_INFERRED: return "inferred";
case COG_PROV_TOLD: return "told";
case COG_PROV_IMPRINTED: return "imprinted";
default: return "unset";
}
}
CogProvClass cog_prov_parse(const char* s) {
if (!s) return COG_PROV_UNSET;
if (!strcmp(s, "observed")) return COG_PROV_OBSERVED;
if (!strcmp(s, "inferred")) return COG_PROV_INFERRED;
if (!strcmp(s, "told")) return COG_PROV_TOLD;
if (!strcmp(s, "imprinted")) return COG_PROV_IMPRINTED;
return COG_PROV_UNSET;
}
/* Locate the GRD1 block in an edge's metadata. It is always the tail; anything
* ahead of it is the edge's pre-existing metadata, preserved verbatim. */
static const char* cog_grd_find(const char* meta) {
if (!meta) return NULL;
size_t ml = strlen(COG_GROUNDING_META_MAGIC);
if (strncmp(meta, COG_GROUNDING_META_MAGIC, ml) == 0) return meta;
const char* p = meta;
while ((p = strstr(p, COG_GROUNDING_META_MAGIC)) != NULL) {
if (p > meta && p[-1] == '\n') return p;
p += ml;
}
return NULL;
}
int cog_grounding_parse(const StoreEdge* e, int64_t now_ms, CogGrounding* out) {
if (!e || !out) return -1;
memset(out, 0, sizeof *out);
/* Two dimensions exist on every edge whether or not grounding has ever been
* established, because they ARE existing substrate rather than new fields:
* associative — the accrued hebb, with its existing dynamics;
* polarity — the signed authored weight. `inhibitory` is precisely this
* distinction crushed to one bit, so it is the seed sign. */
out->associative = e->hebb;
out->polarity = e->inhibitory ? -e->weight : e->weight;
out->prov = COG_PROV_UNSET;
out->ts = e->last_fired > 0 ? e->last_fired : e->updated_at;
const char* blk = cog_grd_find(e->metadata);
if (blk) {
out->present = 1;
char* copy = dupstr(blk);
if (!copy) return -1;
for (char* line = strtok(copy, "\n"); line; line = strtok(NULL, "\n")) {
if (line[0] == '\0') continue;
char tag = line[0];
const char* rest = line + 1; while (*rest == ' ') rest++;
if (tag == 'w') { /* the four numeric dimensions */
double v[4] = {0,0,0,0}; parse_floats(rest, v, 4);
out->factual = v[0]; out->relational = v[1];
out->associative = v[2]; out->polarity = v[3];
} else if (tag == 'k') { /* provenance class */
out->prov = cog_prov_parse(rest);
} else if (tag == 't') { /* timestamp + seq + reinforcements */
double v[3] = {0,0,0}; parse_floats(rest, v, 3);
out->ts = (int64_t)v[0]; out->seq = (int64_t)v[1]; out->reinforcements = v[2];
} else if (tag == 'd') {
double v[3] = {0,0,0}; parse_floats(rest, v, 3);
out->fac_proj = v[0]; out->rel_proj = v[1]; out->cos_angle = v[2];
} else if (tag == 'v') {
snprintf(out->binding_value, sizeof out->binding_value, "%s", rest);
} else if (tag == 'c') {
double v[2] = {0,0}; parse_floats(rest, v, 2);
out->floor_at_record = v[0]; out->rel_floor_at_record = v[1];
} else if (tag == 'p') {
snprintf(out->prev_edge, sizeof out->prev_edge, "%s", rest);
}
}
free(copy);
}
out->agreement = (out->cos_angle > 0) ? 1 : (out->cos_angle < 0 ? -1 : 0);
/* ── DERIVED. Nothing below this line is ever serialized. Recency, decay and
* staleness are read off the curve; storing them is how a number ends up
* asserting something nothing computed (§8.1 / spec §2). */
out->age_ms = (out->ts > 0 && now_ms > out->ts) ? (now_ms - out->ts) : 0;
out->decay = cog_decay_factor(out->age_ms, out->reinforcements, 0.0);
out->factual_now = out->factual * out->decay;
out->relational_now = out->relational * out->decay;
out->associative_now = out->associative * out->decay;
out->stale = (out->present && out->floor_at_record > 0 &&
out->factual_now < out->floor_at_record) ? 1 : 0;
return 0;
}
char* cog_grounding_metadata(const char* base_meta, const CogGrounding* g) {
if (!g) return NULL;
size_t keep = 0;
if (base_meta) {
const char* blk = cog_grd_find(base_meta);
keep = blk ? (size_t)(blk - base_meta) : strlen(base_meta);
while (keep > 0 && base_meta[keep - 1] == '\n') keep--;
}
size_t cap = keep + 1024;
char* buf = malloc(cap); if (!buf) return NULL;
size_t o = 0;
if (keep) { memcpy(buf, base_meta, keep); o = keep; buf[o++] = '\n'; }
o += (size_t)snprintf(buf + o, cap - o, "%s\n", COG_GROUNDING_META_MAGIC);
/* STORED ONLY. factual / relational / associative / polarity / provenance /
* timestamp — plus the joint state a decision saw. No confidence, no
* recency, no staleness, no volatility: those are read off the curve. */
o += (size_t)snprintf(buf + o, cap - o, "w %.9g %.9g %.9g %.9g\n",
g->factual, g->relational, g->associative, g->polarity);
o += (size_t)snprintf(buf + o, cap - o, "k %s\n", cog_prov_name(g->prov));
o += (size_t)snprintf(buf + o, cap - o, "t %lld %lld %.9g\n",
(long long)g->ts, (long long)g->seq, g->reinforcements);
o += (size_t)snprintf(buf + o, cap - o, "d %.9g %.9g %.9g\n",
g->fac_proj, g->rel_proj, g->cos_angle);
o += (size_t)snprintf(buf + o, cap - o, "v %s\n", g->binding_value[0] ? g->binding_value : "-");
o += (size_t)snprintf(buf + o, cap - o, "c %.9g %.9g\n", g->floor_at_record, g->rel_floor_at_record);
if (g->prev_edge[0]) o += (size_t)snprintf(buf + o, cap - o, "p %s\n", g->prev_edge);
(void)o;
return buf;
}
/* ── Consequence, not epsilon. Every test is a floor crossing or a sign change,
* both exact. Ordered so the two INHERENT (discrete) moves are reported in
* preference to the graded ones, because they bypass the salience gate. */
CogSignificance cog_grounding_significant(const CogGrounding* prev,
const CogGrounding* now,
double floor, double rel_floor) {
if (!now) return COG_SIG_NONE;
if (!prev || !prev->present) return COG_SIG_FIRST_RECORD;
/* INHERENT 1 — polarity sign flip. Ignorance and disagreement are different
* states, and support → contradiction is a change of state rather than a
* drift, so no threshold applies. Comparing signs, with zero its own class. */
{
int sp = prev->polarity > 0 ? 1 : (prev->polarity < 0 ? -1 : 0);
int sn = now->polarity > 0 ? 1 : (now->polarity < 0 ? -1 : 0);
if (sp != sn) return COG_SIG_POLARITY_FLIP;
}
/* INHERENT 2 — provenance class change. told → observed is a categorical
* upgrade in what the relation is entitled to, not a movement along an axis. */
if (prev->prov != now->prov) return COG_SIG_PROVENANCE_CHANGE;
/* Crossing an assert floor — the move changes whether this relation can be
* spoken. Compared on the DECAYED values, because that is what the gate reads. */
if ((prev->factual_now >= floor) != (now->factual_now >= floor)) return COG_SIG_FACTUAL_FLOOR;
if ((prev->relational_now >= rel_floor) != (now->relational_now >= rel_floor)) return COG_SIG_RELATIONAL_FLOOR;
/* Flipping factual/relational agreement — the relation stops being "true and
* meaningful" and becomes "true and misapplied", or the reverse. This is the
* 911/CPS contradiction as a measured event rather than a reviewable one. */
if (prev->agreement != now->agreement) return COG_SIG_AGREEMENT_FLIP;
/* A gradient reversing — the evidence stopped pulling the claim toward it and
* began pushing it away, or the same on the values axis. */
if ((prev->fac_proj > 0) != (now->fac_proj > 0)) return COG_SIG_DIRECTION_REVERSAL;
if ((prev->rel_proj > 0) != (now->rel_proj > 0)) return COG_SIG_DIRECTION_REVERSAL;
return COG_SIG_NONE;
}
int cog_significance_inherent(CogSignificance s) {
return (s == COG_SIG_FIRST_RECORD || s == COG_SIG_POLARITY_FLIP ||
s == COG_SIG_PROVENANCE_CHANGE) ? 1 : 0;
}
const char* cog_significance_name(CogSignificance s) {
switch (s) {
case COG_SIG_FIRST_RECORD: return "first-record";
case COG_SIG_POLARITY_FLIP: return "polarity-sign-flip";
case COG_SIG_PROVENANCE_CHANGE: return "provenance-class-change";
case COG_SIG_FACTUAL_FLOOR: return "factual-floor-crossed";
case COG_SIG_RELATIONAL_FLOOR: return "relational-floor-crossed";
case COG_SIG_AGREEMENT_FLIP: return "agreement-sign-flip";
case COG_SIG_DIRECTION_REVERSAL: return "gradient-direction-reversal";
default: return "none";
}
}
/* ── Recording: a NEW edge record. The predecessor is never touched. ────────── */
int cog_grounding_record(EngramPagedStore* s, const StoreEdge* base,
const CogGrounding* g, char* out_id, size_t out_id_cap) {
if (!s || !base || !base->id || !g) return -1;
char root[192];
snprintf(root, sizeof root, "%s", base->id);
char* hash = strchr(root, '#'); if (hash) *hash = '\0';
int seq = (int)g->seq + 1;
char vid[224];
snprintf(vid, sizeof vid, "%s#%d", root, seq);
CogGrounding rec = *g;
rec.seq = seq;
snprintf(rec.prev_edge, sizeof rec.prev_edge, "%s", base->id);
char* meta = cog_grounding_metadata(base->metadata, &rec);
if (!meta) return -1;
StoreEdge e; memset(&e, 0, sizeof e);
e.id = vid; e.from_id = base->from_id; e.to_id = base->to_id;
e.relation = base->relation; e.metadata = meta;
/* The vector IS the weight, so the scalar fields carry their dimensions:
* `weight` the magnitude of polarity, `inhibitory` its sign, `hebb` the
* associative strength. Nothing here is a second copy of a derived value. */
e.weight = rec.polarity < 0 ? -rec.polarity : rec.polarity;
e.inhibitory = rec.polarity < 0 ? 1 : 0;
e.hebb = rec.associative;
e.confidence = base->confidence;
e.created_at = base->created_at;
e.updated_at = rec.ts;
e.last_fired = rec.ts;
e.layer_id = base->layer_id;
int rc = store_put_edge(s, &e);
free(meta);
if (rc != 0) return -1;
if (out_id && out_id_cap) snprintf(out_id, out_id_cap, "%s", vid);
return seq;
}
int cog_grounding_head(EngramPagedStore* s, const char* base_id,
StoreEdge* out, int max_versions) {
if (!s || !base_id || !out) return -1;
if (max_versions <= 0) max_versions = 64;
char root[192]; snprintf(root, sizeof root, "%s", base_id);
char* hash = strchr(root, '#'); if (hash) *hash = '\0';
StoreEdge cur; memset(&cur, 0, sizeof cur);
if (store_get_edge(s, root, &cur) != 1) return -1;
int found = 0;
for (int v = 1; v <= max_versions; v++) {
char vid[224]; snprintf(vid, sizeof vid, "%s#%d", root, v);
StoreEdge nx;
if (store_get_edge(s, vid, &nx) != 1) break;
store_edge_free(&cur); cur = nx; found = v;
}
*out = cur;
return found;
}
/* ── VOLATILITY AND DRIFT: derived from the chain, stored nowhere. The series
* exists only because nothing was destroyed, which is the whole return on
* immutability — a derivative for free. */
int cog_grounding_trajectory(EngramPagedStore* s, const char* base_id,
int64_t now_ms, CogTrajectory* out) {
if (!s || !base_id || !out) return -1;
memset(out, 0, sizeof *out);
char root[192]; snprintf(root, sizeof root, "%s", base_id);
char* hash = strchr(root, '#'); if (hash) *hash = '\0';
double pf = 0, pr = 0, f0 = 0, r0 = 0, fN = 0, rN = 0;
double sum_df = 0, sum_dr = 0;
int n = 0;
for (int v = 0; v <= 64; v++) {
char vid[224];
if (v == 0) snprintf(vid, sizeof vid, "%s", root);
else snprintf(vid, sizeof vid, "%s#%d", root, v);
StoreEdge e;
if (store_get_edge(s, vid, &e) != 1) { if (v) break; else continue; }
CogGrounding g;
if (cog_grounding_parse(&e, now_ms, &g) == 0) {
if (n == 0) { f0 = g.factual; r0 = g.relational; }
else { sum_df += fabs(g.factual - pf); sum_dr += fabs(g.relational - pr); }
pf = g.factual; pr = g.relational; fN = pf; rN = pr;
n++;
}
store_edge_free(&e);
}
out->n_versions = n;
if (n > 1) {
out->factual_volatility = sum_df / (double)(n - 1);
out->relational_volatility = sum_dr / (double)(n - 1);
}
out->factual_drift = fN - f0;
out->relational_drift = rN - r0;
/* "STAYED TRUE, BECAME WRONG" — the event the joint record makes visible and
* that per-dimension versioning would have destroyed: the fact held while
* the meaning degraded. Expressed as signs, so there is no tolerance here
* either: factual did not fall, relational did. */
out->stayed_true_became_wrong =
(n > 1 && out->factual_drift >= 0 && out->relational_drift < 0) ? 1 : 0;
return 0;
}
/* ── Assertion gates on BOTH floors. Traversal is untouched: activation still
* conducts on the factual/associative side, so a relation can remain thinkable
* while ceasing to be assertable. That gap is where the wide angles live. ──── */
int cog_assert_two_axis(EngramPagedStore* s, const char* claim_id,
double floor, double rel_floor, int64_t now_ms,
CogAssertion* out) {
if (!s || !claim_id || !out) return -1;
memset(out, 0, sizeof *out);
if (!(floor > 0)) floor = 0.5;
if (!(rel_floor > 0)) rel_floor = floor;
/* still_held is DERIVED, not a literal (§8.1). Holding is unconditional —
* the store gates nothing — so the question the field actually answers is
* whether the content is present and live. */
StoreNode n;
if (store_get_node(s, claim_id, &n) == 1) { out->still_held = !n.tombstoned; store_node_free(&n); }
else out->still_held = 0;
double best = -1.0;
for (int dir = 0; dir < 2; dir++) {
StoreEdge* edges = NULL; size_t ne = 0;
int rc = dir == 0 ? store_get_edges_from(s, claim_id, &edges, &ne)
: store_get_edges_to (s, claim_id, &edges, &ne);
if (rc < 0) continue;
for (size_t i = 0; i < ne; i++) {
if (edges[i].tombstoned) continue;
CogGrounding g;
if (cog_grounding_parse(&edges[i], now_ms, &g) != 0) continue;
out->n_edges++;
out->found = 1;
if (g.factual_now > best) {
best = g.factual_now;
out->factual = g.factual_now;
out->relational = g.relational_now; /* the SAME edge, not a max */
out->polarity = g.polarity;
out->cos_angle = g.cos_angle;
out->agreement = g.agreement;
out->prov = g.prov;
out->relational_established = g.present;
snprintf(out->best_edge, sizeof out->best_edge, "%s", edges[i].id ? edges[i].id : "");
snprintf(out->binding_value, sizeof out->binding_value, "%s", g.binding_value);
}
}
store_edges_free(edges, ne);
}
/* BOTH floors, and an unestablished relational axis does NOT pass by default
* — defaulting it to passing is the exemption §0 forbids. A negative polarity
* is a relation that actively contradicts and can never license assertion. */
out->may_assert = (out->found && out->relational_established &&
out->polarity > 0 &&
out->factual >= floor && out->relational >= rel_floor) ? 1 : 0;
return 0;
}
/* ═══════════════════════════════════════════════ THE CORRESPONDENCE-LOOP ═════ */ /* ═══════════════════════════════════════════════ THE CORRESPONDENCE-LOOP ═════ */
int engram_correspondence_beat(const GeoDescriptor* region, const float* anchor, int engram_correspondence_beat(const GeoDescriptor* region, const float* anchor,
double outcome_y, CogStance* stance, double outcome_y, CogStance* stance,
int learn, double max_step, CogBeatResult* out) { int learn, double max_step, CogBeatResult* out) {
if (!region || !stance || !out) return -1; if (!region || !stance || !out) return -1;
memset(out, 0, sizeof *out); memset(out, 0, sizeof *out);
if (stance->keystone) { learn = 0; out->wrote_keystone = 1; } /* §6: never write a keystone */ /* 2026-08-16: the keystone block is GONE. It refused to learn about the
* reference frame, which does not make it a good reference — it makes it
* unexaminable, trading circular calibration for an ungroundable one (spec
* §2). Measured cost of the block: on the keystone region the beat reported
* 0.00% brier reduction over n_trials 0 — it never ran, so nothing about the
* self was ever calibrated OR falsifiable. What replaces it is a provenance
* constraint, not a permission: cog_grounding_downstream refuses evidence
* that is downstream of the region being calibrated, for every region alike.
* `wrote_keystone` is retained as a reporting field only and is always 0. */
GeoGradient g; GeoGradient g;
if (engram_think(region, anchor, stance, &g) != 0) return -1; /* PREDICTION */ if (engram_think(region, anchor, stance, &g) != 0) return -1; /* PREDICTION */
+269 -17
View File
@@ -23,7 +23,7 @@
* and a region, and grounded-for-whom. * and a region, and grounded-for-whom.
* *
* PURE + (mostly) READ-ONLY, stdlib + libm only. think() and the warp are pure * PURE + (mostly) READ-ONLY, stdlib + libm only. think() and the warp are pure
* over their inputs. Persistence (Stance <-> StoreNode, grounded-by edges) is the * over their inputs. Persistence (Stance <-> StoreNode, edge grounding vectors) is the
* only part that touches the store, and it is additive / supersede / tombstone — * 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 * never mutate-in-place, never delete. It NEVER touches the live daemon: all
* offline against a scratch store, per the design's rails. * offline against a scratch store, per the design's rails.
@@ -152,30 +152,25 @@ 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). * §5 HOLD vs GROUND vs ASSERT. Holding is unconditional (the store gates nothing).
* Grounding is a RELATION — a "grounded-by" edge, probabilistic, grounded-for-whom. * Grounding is an ATTRIBUTE OF a relation — carried on the edge itself, as a
* The honesty floor is checked only at ASSERTION. * vector (§7). The honesty floor is checked only at ASSERTION, on both axes.
* ═══════════════════════════════════════════════════════════════════════════ */ * ═══════════════════════════════════════════════════════════════════════════ */
#define COG_GROUNDED_BY_RELATION "grounded-by" /* 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" #define COG_SALIENT_TO_RELATION "salient-to"
/* Write a grounded-by edge (additive). weight = grounding ∈(0,1] from the verifier;
* for_whom recorded in edge metadata (grounding is relational). Never a node flag. */
int cog_ground_edge(EngramPagedStore* s, const char* claim_id,
const char* evidence_id, double grounding, const char* for_whom);
/* Write/refresh a salient-to edge: salience is RELATIONAL (grounded-for-whom), /* 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). */ * 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, int cog_salient_edge(EngramPagedStore* s, const char* node_id,
const char* observer_id, double salience); const char* observer_id, double salience);
/* The honesty floor — a QUERY at assertion time, NOT a schema constraint. Reads the
* claim's stored grounded-by edges (for the given observer) and returns:
* 1 = may assert (best grounding >= floor),
* 0 = REFUSE assertion (holds unconditionally; only asserting is gated),
* <0 = error. The content remains held either way. */
int cog_assert_gate(EngramPagedStore* s, const char* claim_id,
const char* for_whom, double floor);
/* ═══════════════════════════════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════════════════════════════
* §4 THE REFLEXIVE CORRESPONDENCE-LOOP — the learning engine. think scores its * §4 THE REFLEXIVE CORRESPONDENCE-LOOP — the learning engine. think scores its
* OWN gradient against outcome, refines the stance on the error, and (optionally) * OWN gradient against outcome, refines the stance on the error, and (optionally)
@@ -209,8 +204,265 @@ int engram_correspondence_beat(const GeoDescriptor* region, const float* anchor,
/* ═══════════════════════════════════════════════════════════════════════════ /* ═══════════════════════════════════════════════════════════════════════════
* §6 METASTABILITY. Keystones (self/values) are read-mostly: the loop reads but * §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. * 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; typedef struct { const char** ids; int n; } CogKeystoneSet;
int cog_is_keystone(const CogKeystoneSet* ks, const CogStance* s); 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 */ #endif /* ENGRAM_COGNITION_H */
+440 -140
View File
@@ -1,61 +1,128 @@
import "../../runtime/eltest.el" import "../../runtime/eltest.el"
// test_transduce.el geometry as a first-class El value, and realizers // test_transduce.el transduction produces a SUBGRAPH, not a point.
// declared in El rather than patched into the runtime.
// //
// WHAT IS ACTUALLY UNDER TEST. Until 2026-08-16 no El ingest path could carry // WHAT IS ACTUALLY UNDER TEST. #144 moved transduction into the language and
// a vector: nodes took text, and geometry was DERIVED from that text. Text was // got the dispatch right: realizers declared in El, resolved by name, no
// therefore the mandatory entry medium, so any non-text modality had to be // runtime patch per modality. It got the RESULT TYPE wrong
// DESCRIBED in prose first and the geometry we reasoned over was the geometry // `transduce(signal, modality) -> Geometry`, one vector per signal.
// OF THE DESCRIPTION, not of the signal. The fix has two halves, and this file
// exercises both:
// //
// 1. Geometry is a VALUE it carries its own width, so nothing has to // One vector is a FINGERPRINT. It can be matched and it can be ranked, and
// assert a width against a string's length. // that is the whole of what it can ever do. It cannot be decomposed, cannot
// 2. A REALIZER is an ordinary El function. `tone_realizer` below is not in // have one part grounded while another is not, and cannot be contradicted in
// the runtime, is not known to the compiler, and is not special in any // one part while holding in another because it has no parts. Treating
// way; it is registered BY NAME and dispatched to through transduce(). // transduction as a CONVERSION (signal in, position out) is the premise this
// That is the load-bearing claim: adding a modality must not require a // file exists to falsify.
// runtime patch, or nothing has actually moved into the language. //
// A song is not a point. It decomposes into pitch, interval, rhythm, harmonic
// function components, each with its own geometry, plus the relations among
// them. THE SONG IS THE STRUCTURE OF THE RELATIONS. So transduction yields a
// Manifold: named components carrying geometry, and typed weighted relations
// between them.
//
// The geometry tests below are UNCHANGED from #144 and still pass, which is
// the point: Geometry was never wrong, it was misplaced. A vector is the right
// representation for a COMPONENT. It was only ever wrong as the representation
// of a whole transduced signal.
// //
// COMPARISON DISCIPLINE IN THIS FILE (measured 2026-08-16, not stylistic): // COMPARISON DISCIPLINE IN THIS FILE (measured 2026-08-16, not stylistic):
// elc lowers `a == b` to a NUMERIC comparison only when both operand names are // elc lowers `a == b` to a NUMERIC comparison only when both operand names are
// in the per-function int-name set, which `let x: Int` populates. A bare call // in the per-function int-name set, which `let x: Int` populates. A bare call
// like `geometry_is(g) == 0` is not a registered name, so it lowers to // like `manifold_size(m) == 5` is not a registered name, so it lowers to
// `str_eq(...)` strcmp on two integers reinterpreted as pointers. `<` and `>` // `str_eq(...)` strcmp on two integers reinterpreted as pointers. `<` and `>`
// lower directly via binop_to_c with no type inference at all, so truthiness is // lower directly via binop_to_c with no type inference at all, so truthiness is
// written `> 0` / `< 1` here, and any exact `==` is done on a value first bound // written `> 0` / `< 1` here, and any exact `==` is done on a value first bound
// through `let x: Int`. // through `let x: Int`.
//
// ONE FURTHER RULE, measured while writing this file: that int-name set LEAKS
// ACROSS `test` BLOCKS. Binding `dn` as a Float in one test and as an Int in
// another silently demoted the Int comparison to str_eq and failed an
// assertion that was arithmetically true. Every Int-bound name compared with
// `==` here is therefore spelled UNIQUELY across the whole file (note_dim,
// iv_dim, ...), rather than reusing a short name per test.
// A realizer, written entirely in El // A DECOMPOSING realizer, written entirely in El
// Maps a "tone" signal into a 4-component geometry. Deliberately trivial // "tone" signals are note letters, e.g. "CEG". This realizer does NOT return
// what is being proven is that an El function can BE a realizer, not that // one vector for the chord. It returns the PARTS one component per note, one
// this is good acoustics. The one real property it has: distinct signals // per interval between adjacent notes and the relations that make those
// produce distinct geometry, so the test can tell transduction from a stub. // parts a chord rather than an unordered bag of pitches.
fn tone_realizer(signal: String) -> Geometry { //
// The interval is deliberately a COMPONENT, not an attribute of a note. An
// interval is a thing with its own geometry that belongs to neither endpoint;
// modelling it as a field on a note is exactly the collapse this change
// rejects, one level down.
fn tone_realizer(signal: String) -> Manifold {
let m: Manifold = manifold_new()
let n: Int = str_len(signal)
let i: Int = 0
while i < n {
let code: Int = str_char_code(signal, i)
let g: Geometry = geometry_new(2)
let s0: Int = geometry_set(g, 0, int_to_float(code))
let s1: Int = geometry_set(g, 1, int_to_float(i))
let idx: Int = manifold_add(m, "note:" + int_to_str(i), "pitch", g)
let f: Int = geometry_free(g)
i = i + 1
}
let j: Int = 1
while j < n {
let a: Int = str_char_code(signal, j - 1)
let b: Int = str_char_code(signal, j)
let lo: String = "note:" + int_to_str(j - 1)
let hi: String = "note:" + int_to_str(j)
let key: String = "interval:" + int_to_str(j - 1) + "-" + int_to_str(j)
let g: Geometry = geometry_new(1)
let s: Int = geometry_set(g, 0, int_to_float(b - a))
let idx: Int = manifold_add(m, key, "interval", g)
let f: Int = geometry_free(g)
let e1: Int = manifold_relate(m, key, "spans", lo, 0.9)
let e2: Int = manifold_relate(m, key, "spans", hi, 0.9)
let e3: Int = manifold_relate(m, lo, "sounds_before", hi, 0.8)
j = j + 1
}
m
}
// A second realizer for a different modality, to prove the registry keys on
// modality and does not just hand back "the last thing registered". Its
// decomposition has a DIFFERENT shape two components, one relation so a
// test can tell the two organs apart by structure alone.
fn pulse_realizer(signal: String) -> Manifold {
let m: Manifold = manifold_new()
let ga: Geometry = geometry_new(1)
let sa: Int = geometry_set(ga, 0, 1.0)
let ia: Int = manifold_add(m, "onset", "event", ga)
let fa: Int = geometry_free(ga)
let gb: Geometry = geometry_new(1)
let sb: Int = geometry_set(gb, 0, 0.0)
let ib: Int = manifold_add(m, "decay", "envelope", gb)
let fb: Int = geometry_free(gb)
let e: Int = manifold_relate(m, "onset", "decays_into", "decay", 0.7)
m
}
// #144's ACTUAL CONTRACT, preserved verbatim as a control: a realizer that
// returns one vector for the whole signal. This is not a strawman it is what
// the merged primitive asked realizers to be. It must now transduce NOTHING.
fn fingerprint_realizer(signal: String) -> Geometry {
let g: Geometry = geometry_new(4) let g: Geometry = geometry_new(4)
let n: Int = str_len(signal) let n: Int = str_len(signal)
let a: Int = geometry_set(g, 0, int_to_float(n)) let a: Int = geometry_set(g, 0, int_to_float(n))
let b: Int = geometry_set(g, 1, int_to_float(n * 2)) let b: Int = geometry_set(g, 1, int_to_float(n * 2))
let c: Int = geometry_set(g, 2, int_to_float(n * 3))
let d: Int = geometry_set(g, 3, int_to_float(n * 4))
g g
} }
// A second realizer for a different modality, to prove the registry keys on // A realizer returning something that is not a value at all.
// modality and does not just hand back "the last thing registered". fn bogus_realizer(signal: String) -> Manifold {
fn pulse_realizer(signal: String) -> Geometry {
let g: Geometry = geometry_new(2)
let a: Int = geometry_set(g, 0, 1.0)
let b: Int = geometry_set(g, 1, 0.0)
g
}
// A deliberately BROKEN realizer: it returns something that is not a Geometry.
// transduce() must not hand this back to a caller as if it were one.
fn bogus_realizer(signal: String) -> Geometry {
return 12345 return 12345
} }
//
// Geometry unchanged from #144. A vector is the right representation for a
// COMPONENT; it was only ever wrong as the representation of a whole signal.
//
test "geometry-is-a-value-with-its-own-width" { test "geometry-is-a-value-with-its-own-width" {
let g: Geometry = geometry_new(8) let g: Geometry = geometry_new(8)
let live: Int = geometry_is(g) let live: Int = geometry_is(g)
@@ -67,17 +134,12 @@ test "geometry-is-a-value-with-its-own-width" {
} }
test "geometry-rejects-nonsense-without-an-arbitrary-bound" { test "geometry-rejects-nonsense-without-an-arbitrary-bound" {
// dim <= 0 is not a width. Note there is deliberately no MAX dim here:
// #141 needed `dim <= 8192` only to bound an allocation sized from a
// caller's claim about a string. A value that carries its own width has
// nothing left to validate, so the only failure left is allocation.
let zero: Geometry = geometry_new(0) let zero: Geometry = geometry_new(0)
let z: Int = geometry_is(zero) let z: Int = geometry_is(zero)
assert z < 1, "dim 0 is not a geometry" assert z < 1, "dim 0 is not a geometry"
let neg: Geometry = geometry_new(-4) let neg: Geometry = geometry_new(-4)
let n: Int = geometry_is(neg) let n: Int = geometry_is(neg)
assert n < 1, "negative dim is not a geometry" assert n < 1, "negative dim is not a geometry"
// Accessors must be total: a non-geometry is 0-width, never a crash.
let nd: Int = geometry_dim(0) let nd: Int = geometry_dim(0)
assert nd < 1, "geometry_dim of a non-geometry is 0" assert nd < 1, "geometry_dim of a non-geometry is 0"
let ni: Int = geometry_is(0) let ni: Int = geometry_is(0)
@@ -105,21 +167,11 @@ test "geometry-components-round-trip" {
} }
test "hex-is-an-edge-adapter-and-derives-its-own-width" { test "hex-is-an-edge-adapter-and-derives-its-own-width" {
// 2 components, little-endian float32: 1.0 = 0000803f, 2.0 = 00000040.
let g: Geometry = geometry_from_f32le_hex("0000803f00000040") let g: Geometry = geometry_from_f32le_hex("0000803f00000040")
let live: Int = geometry_is(g) let live: Int = geometry_is(g)
assert live > 0, "valid hex decodes to a Geometry" assert live > 0, "valid hex decodes to a Geometry"
let d: Int = geometry_dim(g) let hex_dim: Int = geometry_dim(g)
assert d == 2, "width is DERIVED from the input, never supplied" assert hex_dim == 2, "width is DERIVED from the input, never supplied"
let a: Float = geometry_get(g, 0)
let da: Float = a - 1.0
assert da < 0.001, "first component decoded"
assert da > -0.001, "first component decoded"
let b: Float = geometry_get(g, 1)
let db: Float = b - 2.0
assert db < 0.001, "second component decoded"
assert db > -0.001, "second component decoded"
// Egress adapter is the exact inverse.
let back: String = geometry_to_f32le_hex(g) let back: String = geometry_to_f32le_hex(g)
assert str_eq(back, "0000803f00000040"), "hex round-trips exactly" assert str_eq(back, "0000803f00000040"), "hex round-trips exactly"
let freed: Int = geometry_free(g) let freed: Int = geometry_free(g)
@@ -137,98 +189,346 @@ test "hex-rejects-malformed-input" {
assert nh < 1, "non-hex characters are refused" assert nh < 1, "non-hex characters are refused"
} }
test "a-realizer-declared-in-el-is-a-first-class-realizer" {
// THE CLAIM: tone_realizer is an ordinary El function. It is not in the
// runtime and the compiler knows nothing about it. Registering it by name
// is enough to make it the organ for a modality.
let reg: Int = realizer_register("tone", "tone_realizer")
assert reg > 0, "an El fn registers as a realizer by name"
let has: Int = realizer_has("tone")
assert has > 0, "the modality now has an organ"
let g: Geometry = transduce("aaa", "tone")
let live: Int = geometry_is(g)
assert live > 0, "transduce returns real geometry"
let d: Int = geometry_dim(g)
assert d == 4, "the El realizer determined the width, not the runtime"
// str_len("aaa") == 3, so component 0 must be 3.0 proof the signal
// actually reached the El function rather than a stub answering for it.
let c0: Float = geometry_get(g, 0)
let dc: Float = c0 - 3.0
assert dc < 0.001, "the signal reached the El realizer"
assert dc > -0.001, "the signal reached the El realizer"
let freed: Int = geometry_free(g)
}
test "distinct-signals-transduce-to-distinct-geometry" {
let reg: Int = realizer_register("tone", "tone_realizer")
let g1: Geometry = transduce("aa", "tone")
let g2: Geometry = transduce("aaaaa", "tone")
let a: Float = geometry_get(g1, 0)
let b: Float = geometry_get(g2, 0)
let diff: Float = b - a
// 5 - 2 = 3. If transduction were a stub these would be equal.
assert diff > 2.9, "different signals produce different geometry"
assert diff < 3.1, "different signals produce different geometry"
let f1: Int = geometry_free(g1)
let f2: Int = geometry_free(g2)
}
test "the-registry-keys-on-modality" {
let r1: Int = realizer_register("tone", "tone_realizer")
let r2: Int = realizer_register("pulse", "pulse_realizer")
assert r2 > 0, "a second modality registers independently"
let gt: Geometry = transduce("aaa", "tone")
let gp: Geometry = transduce("aaa", "pulse")
let dt: Int = geometry_dim(gt)
let dp: Int = geometry_dim(gp)
assert dt == 4, "tone still routes to its own realizer"
assert dp == 2, "pulse routes to a different realizer"
let f1: Int = geometry_free(gt)
let f2: Int = geometry_free(gp)
}
test "no-organ-is-reported-as-no-organ" {
// A modality with no realizer must transduce to NOTHING. It must never
// fall back to embedding a description of the signal and calling that
// perception that silent substitution is the entire defect this change
// exists to end.
let has: Int = realizer_has("echolocation")
assert has < 1, "unregistered modality has no organ"
let g: Geometry = transduce("anything", "echolocation")
let live: Int = geometry_is(g)
assert live < 1, "no realizer means no geometry, not fake geometry"
}
test "registration-of-an-unresolvable-name-fails-loudly" {
// Reported at the moment of WIRING, not later as "this modality mysteriously
// produces nothing". Distinguishing "no organ" from "broken organ" is the
// lesson that made this whole change necessary.
let bad: Int = realizer_register("ghost", "no_such_function_anywhere")
assert bad < 1, "an unresolvable realizer name is a registration failure"
let has: Int = realizer_has("ghost")
assert has < 1, "and nothing gets registered"
}
test "a-realizer-returning-non-geometry-transduces-nothing" {
let reg: Int = realizer_register("bogus", "bogus_realizer")
assert reg > 0, "the symbol resolves, so registration succeeds"
// ...but the contract is enforced at the boundary, so the caller never
// receives a value that would misbehave far away from here.
let g: Geometry = transduce("x", "bogus")
let live: Int = geometry_is(g)
assert live < 1, "a non-Geometry return transduced nothing"
}
test "norm-lets-a-caller-check-a-realizer-emitted-signal" { test "norm-lets-a-caller-check-a-realizer-emitted-signal" {
let g: Geometry = geometry_new(2) let g: Geometry = geometry_new(2)
let z: Float = geometry_norm(g) let z: Float = geometry_norm(g)
assert z < 0.001, "a fresh geometry is zero — norm says so" assert z < 0.001, "a fresh geometry is zero — norm says so"
let s0: Int = geometry_set(g, 0, 3.0) let s0: Int = geometry_set(g, 0, 3.0)
let s1: Int = geometry_set(g, 1, 4.0) let s1: Int = geometry_set(g, 1, 4.0)
let n: Float = geometry_norm(g) let nrm: Float = geometry_norm(g)
let dn: Float = n - 5.0 let dnorm: Float = nrm - 5.0
assert dn < 0.001, "3-4-5: norm is 5" assert dnorm < 0.001, "3-4-5: norm is 5"
assert dn > -0.001, "3-4-5: norm is 5" assert dnorm > -0.001, "3-4-5: norm is 5"
let freed: Int = geometry_free(g) let freed: Int = geometry_free(g)
} }
//
// Manifold the corrected result of a transduction
//
test "a-manifold-is-a-value-that-holds-parts-and-relations" {
let m: Manifold = manifold_new()
let live: Int = manifold_is(m)
assert live > 0, "manifold_new returns a live Manifold"
let fresh_sz: Int = manifold_size(m)
assert fresh_sz == 0, "a fresh manifold has no components"
let fresh_rc: Int = manifold_rel_count(m)
assert fresh_rc == 0, "a fresh manifold has no relations"
let freed: Int = manifold_free(m)
assert freed > 0, "manifold_free reports what it did"
}
test "manifold-accessors-are-total" {
let ni2: Int = manifold_is(0)
assert ni2 < 1, "manifold_is of a non-manifold is 0"
let ns: Int = manifold_size(0)
assert ns < 1, "manifold_size of a non-manifold is 0"
let nf2: Int = manifold_free(0)
assert nf2 < 1, "manifold_free of a non-manifold is a no-op"
let k: String = manifold_key(0, 0)
assert str_eq(k, ""), "manifold_key of a non-manifold is empty, never a crash"
}
test "components-are-addressed-by-key-not-by-index" {
// The key is what survives persistence: a component becomes a node, and it
// is separately groundable precisely because it is separately NAMED.
let m: Manifold = manifold_new()
let g: Geometry = geometry_new(1)
let s: Int = geometry_set(g, 0, 7.0)
let first_idx: Int = manifold_add(m, "rhythm", "temporal", g)
assert first_idx == 0, "the first component is index 0"
let found_idx: Int = manifold_index_of(m, "rhythm")
assert found_idx == 0, "a component is found by its key"
let missing: Int = manifold_index_of(m, "never_added")
assert missing < 0, "an unknown key resolves to -1, not to component 0"
let role: String = manifold_role(m, 0)
assert str_eq(role, "temporal"), "a component carries what KIND of part it is"
let f: Int = geometry_free(g)
let fm: Int = manifold_free(m)
}
test "a-duplicate-key-is-refused-because-addressing-must-be-unambiguous" {
let m: Manifold = manifold_new()
let g: Geometry = geometry_new(1)
let ok_idx: Int = manifold_add(m, "pitch", "spectral", g)
assert ok_idx == 0, "first add succeeds"
let dup: Int = manifold_add(m, "pitch", "spectral", g)
assert dup < 0, "two components answering to one name is not an addressing scheme"
let dup_sz: Int = manifold_size(m)
assert dup_sz == 1, "and the duplicate did not land"
let f: Int = geometry_free(g)
let fm: Int = manifold_free(m)
}
test "a-part-with-no-geometry-is-not-a-part" {
let m: Manifold = manifold_new()
let bad: Int = manifold_add(m, "ghost", "none", 0)
assert bad < 0, "a non-Geometry is refused as a component"
let empty_key: Int = manifold_add(m, "", "none", geometry_new(1))
assert empty_key < 0, "an unaddressable component is refused"
let none_sz: Int = manifold_size(m)
assert none_sz < 1, "nothing landed"
let fm: Int = manifold_free(m)
}
test "an-edge-to-a-nonexistent-endpoint-is-refused-not-dropped" {
// A decomposition that silently loses edges is indistinguishable from one
// that never had them.
let m: Manifold = manifold_new()
let g: Geometry = geometry_new(1)
let a: Int = manifold_add(m, "here", "part", g)
let dangling: Int = manifold_relate(m, "here", "points_at", "nowhere", 0.5)
assert dangling < 1, "an edge to an unknown target is refused"
let backwards: Int = manifold_relate(m, "nowhere", "points_at", "here", 0.5)
assert backwards < 1, "an edge from an unknown source is refused"
let dang_rc: Int = manifold_rel_count(m)
assert dang_rc < 1, "and no relation was recorded"
let f: Int = geometry_free(g)
let fm: Int = manifold_free(m)
}
test "a-component-owns-its-geometry-independently-of-the-caller" {
// manifold_add COPIES. Freeing the caller's vector must not disturb the
// component, or a decomposition would be unusable the moment it was built.
let m: Manifold = manifold_new()
let g: Geometry = geometry_new(2)
let s0: Int = geometry_set(g, 0, 42.0)
let idx: Int = manifold_add(m, "part", "kind", g)
let freed: Int = geometry_free(g)
assert freed > 0, "the caller freed its own vector"
let back: Geometry = manifold_geometry(m, 0)
let live: Int = geometry_is(back)
assert live > 0, "the component still has geometry"
let v: Float = geometry_get(back, 0)
let dv: Float = v - 42.0
assert dv < 0.001, "and it is the right geometry"
assert dv > -0.001, "and it is the right geometry"
let fb: Int = geometry_free(back)
let fm: Int = manifold_free(m)
}
//
// transduce signal in, SUBGRAPH out
//
test "a-realizer-declared-in-el-is-a-first-class-realizer" {
// THE CLAIM, unchanged from #144: tone_realizer is an ordinary El function.
// It is not in the runtime and the compiler knows nothing about it.
// Registering it by name is enough to make it the organ for a modality.
let reg: Int = realizer_register("tone", "tone_realizer")
assert reg > 0, "an El fn registers as a realizer by name"
let has: Int = realizer_has("tone")
assert has > 0, "the modality now has an organ"
let m: Manifold = transduce("CEG", "tone")
let live: Int = manifold_is(m)
assert live > 0, "transduce returns a real Manifold"
let fm: Int = manifold_free(m)
}
test "transduction-decomposes-a-signal-into-parts" {
// THE CENTRAL CLAIM. "CEG" is three notes. What comes back is not one
// vector standing for a chord it is five addressable parts (three notes,
// two intervals) and six relations. A fingerprint has one part by
// construction and could not express this at any width.
let reg: Int = realizer_register("tone", "tone_realizer")
let m: Manifold = transduce("CEG", "tone")
let ceg_sz: Int = manifold_size(m)
assert ceg_sz == 5, "three notes and two intervals are five distinct parts"
let ceg_rc: Int = manifold_rel_count(m)
assert ceg_rc == 6, "and the parts stand in six stated relations"
// Every part is independently addressable BY NAME.
let n0: Int = manifold_index_of(m, "note:0")
assert n0 > -1, "the first note is addressable on its own"
let n2: Int = manifold_index_of(m, "note:2")
assert n2 > -1, "so is the third"
let iv: Int = manifold_index_of(m, "interval:0-1")
assert iv > -1, "so is the interval between the first two"
let fm: Int = manifold_free(m)
}
test "each-part-carries-its-own-geometry" {
let reg: Int = realizer_register("tone", "tone_realizer")
let m: Manifold = transduce("CEG", "tone")
// 'C' is 67. The note component's geometry is the note's, not the chord's.
let note_i: Int = manifold_index_of(m, "note:0")
let gn: Geometry = manifold_geometry(m, note_i)
let note_dim: Int = geometry_dim(gn)
assert note_dim == 2, "a note component has the width its realizer gave it"
let pitch: Float = geometry_get(gn, 0)
let dpitch: Float = pitch - 67.0
assert dpitch < 0.001, "and it is C, so the signal reached the El realizer"
assert dpitch > -0.001, "and it is C, so the signal reached the El realizer"
// Parts may have DIFFERENT widths. A single vector per signal cannot
// represent parts of unequal dimensionality at all.
let iv_i: Int = manifold_index_of(m, "interval:0-1")
let gi: Geometry = manifold_geometry(m, iv_i)
let iv_dim: Int = geometry_dim(gi)
assert iv_dim == 1, "an interval component has its own, different width"
let f1: Int = geometry_free(gn)
let f2: Int = geometry_free(gi)
let fm: Int = manifold_free(m)
}
test "the-relations-are-content-no-single-part-carries" {
// THE POINT OF THE WHOLE CHANGE. C->E is two semitones. That "2" is not a
// property of C and not a property of E; it exists only BETWEEN them. A
// representation with no relations cannot hold it, which is why collapsing
// a signal to one vector does not merely lose resolution it loses a
// category of content.
let reg: Int = realizer_register("tone", "tone_realizer")
let m: Manifold = transduce("CEG", "tone")
let step_i: Int = manifold_index_of(m, "interval:0-1")
let gi: Geometry = manifold_geometry(m, step_i)
let step: Float = geometry_get(gi, 0)
let dstep: Float = step - 2.0
assert dstep < 0.001, "C to E is two semitones"
assert dstep > -0.001, "C to E is two semitones"
// And the interval is WIRED to both endpoints, so the structure says which
// two things it is the interval between.
let spans: Int = 0
let span_rc: Int = manifold_rel_count(m)
let k: Int = 0
while k < span_rc {
let rn: String = manifold_rel_name(m, k)
let rf: String = manifold_rel_from(m, k)
if str_eq(rn, "spans") {
if str_eq(rf, "interval:0-1") { spans = spans + 1 }
}
k = k + 1
}
assert spans == 2, "the interval is related to both notes it spans"
let fg: Int = geometry_free(gi)
let fm: Int = manifold_free(m)
}
test "relation-weight-is-the-grounding-carried-on-the-edge" {
// correspondence-and-censorship.md §1: grounding is an attribute of the
// edge and it IS the weight one quantity, not a score computed beside
// it. A realizer states a relation and its weight is the claim.
let reg: Int = realizer_register("tone", "tone_realizer")
let m: Manifold = transduce("CE", "tone")
let ce_rc: Int = manifold_rel_count(m)
assert ce_rc == 3, "one interval yields two spans and one ordering"
let found_w: Int = 0
let k: Int = 0
while k < ce_rc {
let rn: String = manifold_rel_name(m, k)
if str_eq(rn, "sounds_before") {
let w: Float = manifold_rel_weight(m, k)
let dw: Float = w - 0.8
if dw < 0.001 { if dw > -0.001 { found_w = found_w + 1 } }
}
k = k + 1
}
assert found_w == 1, "the ordering relation carries the weight its realizer stated"
let fm: Int = manifold_free(m)
}
test "distinct-signals-decompose-differently" {
let reg: Int = realizer_register("tone", "tone_realizer")
let m2: Manifold = transduce("CE", "tone")
let m3: Manifold = transduce("CEG", "tone")
let two_sz: Int = manifold_size(m2)
let three_sz: Int = manifold_size(m3)
assert two_sz == 3, "two notes decompose into two notes and one interval"
assert three_sz == 5, "three notes decompose into three notes and two intervals"
// Structure differs, not just position: fingerprints of a two-note and a
// three-note signal have identical shape and differ only numerically.
let two_rc: Int = manifold_rel_count(m2)
let three_rc: Int = manifold_rel_count(m3)
assert two_rc < three_rc, "and the relational structure itself differs"
let f2: Int = manifold_free(m2)
let f3: Int = manifold_free(m3)
}
test "the-registry-keys-on-modality" {
let r1: Int = realizer_register("tone", "tone_realizer")
let rp: Int = realizer_register("pulse", "pulse_realizer")
assert rp > 0, "a second modality registers independently"
let mt: Manifold = transduce("CEG", "tone")
let mp: Manifold = transduce("CEG", "pulse")
let tone_sz: Int = manifold_size(mt)
let pulse_sz: Int = manifold_size(mp)
assert tone_sz == 5, "tone still routes to its own realizer"
assert pulse_sz == 2, "pulse routes to a different realizer, with its own decomposition"
let onset: Int = manifold_index_of(mp, "onset")
assert onset > -1, "and to that realizer's own component vocabulary"
let f1: Int = manifold_free(mt)
let f2: Int = manifold_free(mp)
}
test "no-organ-is-reported-as-no-organ" {
// A modality with no realizer must transduce to NOTHING. It must never
// fall back to embedding a description of the signal and calling that
// perception that silent substitution is the original defect.
let has: Int = realizer_has("echolocation")
assert has < 1, "unregistered modality has no organ"
let m: Manifold = transduce("anything", "echolocation")
let live: Int = manifold_is(m)
assert live < 1, "no realizer means no manifold, not a fake one"
}
test "registration-of-an-unresolvable-name-fails-loudly" {
let bad: Int = realizer_register("ghost", "no_such_function_anywhere")
assert bad < 1, "an unresolvable realizer name is a registration failure"
let has: Int = realizer_has("ghost")
assert has < 1, "and nothing gets registered"
}
test "a-fingerprint-realizer-transduces-nothing" {
// THE SUPERSESSION OF #144, asserted directly. fingerprint_realizer is
// exactly what the merged primitive asked a realizer to be: signal in, one
// Geometry out. It resolves, so registration succeeds the organ is
// present. But it does not decompose, so it does not transduce.
//
// This is a deliberate hard failure. "No organ" and "an organ that only
// fingerprints" must not be indistinguishable, which is the same
// distinction realizer_register already draws between an absent and a
// broken organ. A modality with genuinely one part says so with
// manifold_single, and is then visibly a size-1 manifold.
let reg: Int = realizer_register("fingerprint", "fingerprint_realizer")
assert reg > 0, "the symbol resolves, so registration succeeds"
let m: Manifold = transduce("x", "fingerprint")
let live: Int = manifold_is(m)
assert live < 1, "a single vector is not a transduction"
}
test "a-realizer-returning-nonsense-transduces-nothing" {
let reg: Int = realizer_register("bogus", "bogus_realizer")
assert reg > 0, "the symbol resolves, so registration succeeds"
let m: Manifold = transduce("x", "bogus")
let live: Int = manifold_is(m)
assert live < 1, "a non-Manifold return transduced nothing"
}
test "the-one-part-case-is-a-size-one-manifold-not-a-bare-vector" {
// Some modalities really do have one part. That is a manifold of size 1
// a special case of decomposition, not a parallel path back to a
// fingerprint. Anything reading it still asks manifold_size and still gets
// a real answer, and a second part can be added later without changing the
// type of the thing.
let g: Geometry = geometry_new(3)
let s: Int = geometry_set(g, 0, 5.0)
let m: Manifold = manifold_single("level", "scalar", g)
let live: Int = manifold_is(m)
assert live > 0, "manifold_single yields a real Manifold"
let one_sz: Int = manifold_size(m)
assert one_sz == 1, "of size one — visibly degenerate, not hidden"
let idx: Int = manifold_index_of(m, "level")
assert idx == 0, "and its one part is still addressable by name"
let f: Int = geometry_free(g)
let fm: Int = manifold_free(m)
}