geometry: disagreement belongs on the edge, not averaged into the region
El SDK CI - dev / build-and-test (pull_request) Failing after 4m8s

co_registration is corr(hebb strength, semantic proximity) over a region's
internal edges. Whether use and meaning agree is a property of EACH EDGE;
the correlation averages it into one scalar per region, so a region holding
one violently disagreeing edge beside one violently agreeing edge reports
~0. The disagreements cancel and the summary destroys exactly what it was
built to reveal — the mean-versus-min error, in different clothes.

Measured: 375 live neighborhoods, 340 positive, 31 AT ZERO, 4 negative.
Read as a count that says 'four things to be curious about'. Read correctly
it says four were lopsided enough to survive averaging, and the 31 zeros
are where opposing sites cancelled.

The loop computing the aggregate already had both halves per edge — w and
cs — and threw them away. Now:
    discord = z(semantic proximity) - z(association strength)
standardized within the region from accumulators already gathered. No
second statistic, no constant, no threshold; |discord| IS the nucleation
strength. >0 near in meaning yet unlinked by use; <0 linked by use yet far
in meaning. Both surprising.

This also removes the reason curiosity looked like a search problem. With a
per-region number the only way to find sites is to enumerate regions — I
wrote exactly that sweep, and it is a supervisor walking the structure,
O(n) per call, fine at 375 and impossible at a million. Nothing in a mind
scans its neighborhoods to find what is surprising; the surprise captures
attention. That sweep is reverted here.

co_registration is deprecated, not deleted: it is embedded in the persisted
GEO1 blob and removing it is a format migration that must not ride along.
Nothing new may read it.
This commit is contained in:
Neuron
2026-08-16 13:15:05 -05:00
parent 1be219c4ca
commit a8845e1d39
5 changed files with 208 additions and 10 deletions
+134 -2
View File
@@ -1172,6 +1172,128 @@ void http_set_handler(el_val_t name) {
pthread_mutex_unlock(&_http_handler_mu);
}
/* ── Ambient consolidation: dreaming ────────────────────────────────────────
*
* Dreaming is not sleep, and it is not scheduled. A brain has no cron job.
* The default mode network is ANTICORRELATED WITH TASK ENGAGEMENT: attention
* drops, it activates hundreds of times a day, for seconds at a time.
* Daydreaming and sleep-dreaming are one process at different depths, and the
* depth is set by how much capacity is unclaimed, not by a time of day.
*
* WHY THIS EXISTS (2026-08-16). Consolidation had no owner, so it was
* implemented at every site that needed a piece of it measured: soul's
* in-process awareness loop, three POST beats on the engram, a 600s ticker,
* two resident Python services, and three cron entries at 23:55 / 06:00 /
* 08:30. That last trio is a sleep cycle written as crontab. Seven systems
* dreaming into one graph with no owner for dreaming is what crashed soul on
* this date; the contention was the symptom of the missing owner.
*
* Every ticker is the diagnostic. A StartInterval, an Hour/Minute, a
* POST-to-beat each marks a place where an intrinsic rhythm was replaced by
* an external clock, which is a supervisor invented for something that should
* be a property of the substrate.
*
* The engagement signal already existed and needed no invention:
* _http_conn_active under _http_conn_mu is exactly "capacity currently
* claimed." The dreamer waits for it to reach zero and yields the moment it
* does not. That is the anticorrelation, literally rather than by analogy.
*
* CONTRACT: the handler performs ONE step and returns. The runtime cannot
* preempt El code, so interruptibility is at step granularity a step must
* be small enough that a request arriving mid-step is not made to wait. It
* returns non-zero if it did work. Returning zero means "nothing to
* consolidate," and the dreamer then blocks until activity changes rather
* than spinning. There is no timer anywhere in this file for this purpose,
* and adding one would be the defect described above.
*
* `depth` is derived from CONTINUOUS unclaimed time: a brief gap affords a
* shallow recombination; a long quiet affords a deep one. Same process. Sleep
* is where unclaimed capacity is greatest, not where the process lives. */
typedef el_val_t (*dream_fn)(el_val_t depth);
static char* _dream_handler = NULL;
static int _dream_started = 0;
static int64_t dream_now_ms(void) {
struct timespec ts;
#if defined(CLOCK_MONOTONIC)
clock_gettime(CLOCK_MONOTONIC, &ts);
#else
clock_gettime(CLOCK_REALTIME, &ts);
#endif
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
static dream_fn dream_lookup(void) {
dream_fn out = NULL;
pthread_mutex_lock(&_http_handler_mu);
if (_dream_handler && *_dream_handler)
out = (dream_fn)dlsym(RTLD_DEFAULT, _dream_handler);
pthread_mutex_unlock(&_http_handler_mu);
return out;
}
static void* dream_loop(void* unused) {
(void)unused;
int64_t idle_since = 0;
for (;;) {
/* Wait for unclaimed capacity. Any engagement resets the depth clock:
* depth reflects CONTINUOUS quiet, so an interruption starts it over. */
pthread_mutex_lock(&_http_conn_mu);
while (_http_conn_active > 0) {
idle_since = 0;
pthread_cond_wait(&_http_conn_cv, &_http_conn_mu);
}
pthread_mutex_unlock(&_http_conn_mu);
int64_t now = dream_now_ms();
if (idle_since == 0) idle_since = now;
int64_t quiet = now - idle_since;
/* Depth from unclaimed capacity. Not a schedule — a gradient. */
int depth = quiet < 1000 ? 1 /* a gap between requests */
: quiet < 30000 ? 2 /* a lull */
: quiet < 300000 ? 3 /* sustained quiet */
: 4; /* deep: the "sleep" case */
dream_fn fn = dream_lookup();
if (!fn) return NULL; /* handler vanished: stop, do not spin */
el_val_t did_work = fn((el_val_t)depth);
if (!(int64_t)did_work) {
/* Nothing to consolidate. Do NOT poll — block until engagement
* changes. If there is nothing to dream about, wait for something
* to happen rather than asking again on a timer. */
pthread_mutex_lock(&_http_conn_mu);
while (_http_conn_active == 0)
pthread_cond_wait(&_http_conn_cv, &_http_conn_mu);
pthread_mutex_unlock(&_http_conn_mu);
idle_since = 0;
}
}
return NULL;
}
/* dream_set_handler(name) — register the consolidation step and start
* dreaming. Resolves by dlsym against the running binary, the same mechanism
* http_set_handler uses: every El `fn name(...)` compiles to a global C symbol
* with that exact name. Inert until called, so a program that never registers
* one simply never dreams and pays nothing. */
void dream_set_handler(el_val_t name) {
const char* n = EL_CSTR(name);
pthread_mutex_lock(&_http_handler_mu);
free(_dream_handler);
_dream_handler = el_strdup(n ? n : "");
int start = (!_dream_started && n && *n && dlsym(RTLD_DEFAULT, n) != NULL);
if (start) _dream_started = 1;
pthread_mutex_unlock(&_http_handler_mu);
if (start) {
pthread_t tid;
if (pthread_create(&tid, NULL, dream_loop, NULL) == 0) pthread_detach(tid);
else { pthread_mutex_lock(&_http_handler_mu); _dream_started = 0; pthread_mutex_unlock(&_http_handler_mu); }
}
}
static http_handler_fn http_lookup_active(void) {
http_handler_fn out = NULL;
pthread_mutex_lock(&_http_handler_mu);
@@ -1738,7 +1860,12 @@ static void* http_worker(void* arg) {
/* release a slot */
pthread_mutex_lock(&_http_conn_mu);
_http_conn_active--;
pthread_cond_signal(&_http_conn_cv);
/* BROADCAST, not signal (2026-08-16): the ambient consolidation thread
* waits on this same condvar for _http_conn_active == 0. cond_signal wakes
* exactly one waiter, so the accept loop could take every wake and starve
* the dreamer indefinitely. Both wait sites re-check their predicate in a
* while loop, so broadcasting is safe. */
pthread_cond_broadcast(&_http_conn_cv);
pthread_mutex_unlock(&_http_conn_mu);
return NULL;
}
@@ -2083,7 +2210,12 @@ static void* http_worker_v2(void* arg) {
el_closesocket(fd);
pthread_mutex_lock(&_http_conn_mu);
_http_conn_active--;
pthread_cond_signal(&_http_conn_cv);
/* BROADCAST, not signal (2026-08-16): the ambient consolidation thread
* waits on this same condvar for _http_conn_active == 0. cond_signal wakes
* exactly one waiter, so the accept loop could take every wake and starve
* the dreamer indefinitely. Both wait sites re-check their predicate in a
* while loop, so broadcasting is safe. */
pthread_cond_broadcast(&_http_conn_cv);
pthread_mutex_unlock(&_http_conn_mu);
return NULL;
}