Commit Graph

419 Commits

Author SHA1 Message Date
will.anderson d4a04bb944 Merge pull request 'swarm: native interruptibility for dispatched agent workers' (#107) from worktree-agent-a6177cda24c71d1df into dev
El SDK CI - dev / build-and-test (push) Failing after 3m55s
2026-08-15 19:57:10 +00:00
will.anderson 905c707d68 Merge pull request 'peripheral: own-core, consent-gated I/O organ (mic/camera/speaker)' (#112) from worktree-agent-af50f3458d7754f19 into dev
El SDK CI - dev / build-and-test (push) Failing after 3m42s
2026-08-15 19:55:51 +00:00
will.anderson 56455740e3 Merge pull request 'elp: native audio/image efferent surfaces + projector proof-of-shape' (#111) from worktree-agent-aaf04b0a9714c4070 into dev
El SDK CI - dev / build-and-test (push) Failing after 4m4s
2026-08-15 19:55:38 +00:00
will.anderson 840e54c7ac Merge pull request 'elp: native speech synthesis + voice-imitation faculty' (#110) from worktree-agent-acc02900ef4ade35e into dev
El SDK CI - dev / build-and-test (push) Failing after 4m13s
2026-08-15 19:55:20 +00:00
will.anderson 5c6da24033 Merge pull request 'spec: grounded edge-propagation (task #50) — gated design artifact' (#108) from worktree-agent-a6577c8211c332c5b into dev
El SDK CI - dev / build-and-test (push) Failing after 3m42s
2026-08-15 19:55:02 +00:00
will.anderson 09ae14a970 Merge pull request 'fix: float arithmetic codegen (segfault/garbage) and math_log aliasing' (#104) from worktree-agent-a456e0cf8cd2ee361 into dev
El SDK CI - dev / build-and-test (push) Failing after 3m57s
2026-08-15 19:54:46 +00:00
will.anderson 7b3f8f2ce8 Merge pull request 'sandbox: multi-repo stack worktree composer (el-stack / neuron-stack)' (#101) from worktree-agent-ac2381b0b9615ab20 into dev
El SDK CI - dev / build-and-test (push) Failing after 3m50s
2026-08-15 19:54:29 +00:00
will.anderson 45f64f3fac Merge pull request 'elp: native-EL language faculty — comprehension, propositions, multilingual, translation' (#100) from integration/langfaculty-20260814 into dev
El SDK CI - dev / build-and-test (push) Failing after 4m13s
2026-08-15 19:54:14 +00:00
will.anderson fdf0d6cb64 Merge pull request 'nsbx: one-command dev onboarding (branch + worktree + isolated engram)' (#99) from feat/nsbx-dev-env into dev
El SDK CI - dev / build-and-test (push) Failing after 13m56s
2026-08-15 19:52:35 +00:00
bigmerge 6f3d692784 Add peripheral — own-core, consent-gated I/O organ
El SDK CI - dev / build-and-test (pull_request) Failing after 14m23s
939-line Swift I/O organ (mic/camera capture, speaker playback via
AVFoundation/CoreAudio), own-core LPC voice synthesis/imitation,
consent-gating, and full-duplex barge-in conversation — closing the
hear -> understand -> speak loop entirely on-device.

.gitignore in this dir already excludes bin/ (build output), out/
(captured media), and .consent.json/.resume.json (local runtime state),
so only src + README + .gitignore are committed here.
2026-08-15 14:28:14 -05:00
bigmerge b7e2c580a8 Add native speech synthesis and voice-imitation faculty
El SDK CI - dev / build-and-test (pull_request) Successful in 6m28s
speech.el: formant/glottal integer DSP synthesis + voice-analyze-by-
imitation. voice-profile.el / voice-ingest.el: voice-profile plumbing.
accent.el: British-RP as an ingested transform-geometry (explicitly marked
provisional/citation-pending by its own comments). organ-read.el:
engram read-through for the speech organ. Includes demo/test drivers and
non-personal reference data (British-RP phonetics/lexicon derived data,
a public-domain LibriVox RP reference recording).

Deliberately excludes elp/data/live/ (raw recorded voice + face-photo
samples of the repo owner) and the will-*.{json,psv} derived voiceprint
files — personal biometric data that shouldn't be committed to a shared
repo without an explicit decision from the owner. Also excludes this
worktree's elp/src/surface-profile.el, which diverges from the copy in
other worktrees (agent-aaf04b0a9714c4070, main) — needs manual
reconciliation before landing, left out here to avoid silently picking a
version.
2026-08-15 14:27:52 -05:00
bigmerge 827257d3a4 Remove __pycache__ .pyc files accidentally included in the projector commit
El SDK CI - dev / build-and-test (pull_request) Successful in 6m28s
2026-08-15 14:27:23 -05:00
bigmerge 4bbfdcceff Add native audio/image efferent surfaces + projector proof-of-shape
audio-surface.el / image-surface.el: own-core additive-synthesis WAV and
raster-PNG renderers (integer-only DSP, since EL has no floats), rendered
from learned engram signatures via a pluggable surface-profile
abstraction (surface-profile.el). audio-demo.el / image-demo.el are
drivers. NOTE: demo files hardcode absolute paths to this worktree's own
directory — will need a path fixup before landing.

elp/projector/ is a Python package the author's own README marks as
"STAGING/PROOF-OF-SHAPE — not the deliverable", superseded by the native
.el surface-profile work above; kept as a validated architecture proof.
Generated output (elp/faculty/{out,sig}, elp/projector/out,
__pycache__) intentionally excluded.
2026-08-15 14:26:59 -05:00
bigmerge 5f3ddb8b8d Add grounded edge-propagation spec (task #50): core algorithm, proof harness, gated integration patches
El SDK CI - dev / build-and-test (pull_request) Failing after 14m31s
LTP/LTD-style belief grounding propagated along graph edges, with
union-find independence-guarded corroboration. Package: core C algorithm
(gep_core.h), a self-contained deterministic proof harness with recorded
output, staged runtime integration, and gated .el patches for the beat
hook and HTTP route.

Per the author's own LEDGER.md: built + proven on a clone, GATED pending
the engine/HNSW cutover — not wired into the live beat or routes.
Preserved here as a spec/reference artifact, not a request to merge into
the live path.
2026-08-15 14:26:06 -05:00
bigmerge 708722b7ff Add native interruptibility for dispatched agent workers
El SDK CI - dev / build-and-test (pull_request) Failing after 14m43s
Cancellation-token control channel checked at every step boundary lets a
coordinator PAUSE/RESUME/REDIRECT/KILL a running worker mid-task instead of
waiting for the whole (possibly wrong) plan to finish. Bounded purviews
mean no half-committed state to unwind on interrupt. Includes a proof
harness (proof.el, run.sh) comparing a broken non-interruptible worker
against the new one under identical kill/redirect/pause timing.

Distinct from the already-preserved swarm-ccr orchestrator (fan-out/
converge dispatch): this is single-worker interruptibility, a
complementary mechanism, not a duplicate.
2026-08-15 14:25:55 -05:00
bigmerge 2f832c8def Fix float arithmetic codegen and math_log aliasing
El SDK CI - dev / build-and-test (pull_request) Failing after 10m8s
Float + previously fell through to string concat (segfault); -, *, /, %
operated on raw IEEE-754 bit patterns as integers (garbage results). Floats
are now tracked via a __float_names typed-binding set (parallel to the
existing int-tracking scheme) and arithmetic is emitted as real C double
ops.

Also fixes math_log, which was wrongly aliased to natural log (duplicating
math_ln) — now uses log10 — and adds the missing <math.h> include. Rebuilt
elc binary included.
2026-08-15 14:24:14 -05:00
bigmerge 7e4b21c779 Add sandbox: multi-repo stack worktree composer (el-stack / neuron-stack)
El SDK CI - dev / build-and-test (pull_request) Successful in 6m19s
Assembles every constituent repo of a stack into one combined worktree
workspace, laid out at natural relpaths so cross-repo ../foundation/el
imports resolve to the sandbox copy. Sibling of nsbx; pure bash + git
worktree; never touches live :8742/:7770; isolated engram delegated to nsbx.
2026-08-15 00:55:22 -05:00
Neuron 54378c7355 elp(translate): refactor to geometry-native concept-pivot
El SDK CI - dev / build-and-test (pull_request) Successful in 6m57s
Drop the bilingual-string-table framing and the external-encoder plan (both
wrong). Translation now routes source-lexicon -> concept-frame (language-
invariant, in the engram concept geometry) -> target-realizer, exactly as the
ELP was designed: a word resolves to the CONCEPT it denotes via its own
language's lexicon (a monolingual step — the engram nearest-region ranker only
disambiguates senses within one language, so an English-trained embedder is
fine and never compares 'ocean'~'oceano' as strings). The concept node is the
shared pivot; its manifold location is the meaning.

- Pronouns route through the NATIVE concept pivot (cp_pron_concept ->
  cp_rom_pron_surface) instead of an ad-hoc EN->tgt string map.
- lemma_for_concept / noun_for_concept are each target language's own
  CONCEPT->SURFACE lexicon (the mirror of comprehend's SURFACE->CONCEPT).
- Fidelity is concept-preservation (concept_frame fingerprint), not string
  cosine against an external multilingual model.
- Plural article agreement fixed (las/los, as/os).

Verified: 'You never fought the ocean.' -> ES 'Usted nunca luchó el océano.'
concept-frame pivot 'pred=fight patient=ocean pol=neg' realizes to ES+PT from
one parse; nunca holds 3/3. Gaps unchanged: PT verb conjugation fallback,
adjunct/subordinator concepts not yet in-frame.
2026-08-14 17:53:00 -05:00
Neuron 640e8799e5 elp(translate): normalize EN irregular pasts before the lemma bridge
comprehend lemmatizes some irregulars (fought->fight) but not all (broke);
tr_norm_verb covers the poem's remainder so affirmative content verbs route
(ES 'Yo broo' -> 'Yo rompo'). Negation lines unchanged and still correct.
2026-08-14 17:38:13 -05:00
Neuron 9b63a2a23b elp(translate): EN->ES/PT geometric-free translation faculty
Adds the missing middle of the ELP: a deterministic EN-content-lemma ->
target-lemma bridge (translate.el) on top of comprehend.el (parse) and
realizer.el (inflect). English-only engram geometry cannot route
cross-lingually and vocabulary-XX.el carries no en_translation glosses, so
the honest no-LLM bridge is a wired lexicon (poem coverage; OOV passes
through). SACRED polarity/neg_word are carried untouched: 'never' localizes
to a negator ('nunca'), never to a content lemma.

Additive realizer extensions: agent_person/agent_number recognize Romance
target pronouns; the non-EN negation branch surfaces a carried neg_word
instead of the generic negator.

Verified on the real toolchain (elc->cc->run):
  'You never fought the ocean.' -> ES 'Tú nunca luchaste el océano.'
  'I never saw the breaking.'   -> ES 'Yo nunca vi la ruptura.'
nunca holds 3/3 negation lines. Known gaps: PT verb conjugation fallback
(lutarred), irregular EN lemma (broke->break), adjunct/subordinator passthrough.
2026-08-14 17:37:01 -05:00
bigmerge 6660becfdb nsbx: add one-command dev onboarding (branch + worktree + isolated engram)
El SDK CI - dev / build-and-test (pull_request) Successful in 6m30s
Add 'nsbx dev <name>' / 'nsbx dev-down <name>' plus a Makefile so a newcomer
goes from clone to coding on an isolated cloned engram in one command. The
worktree is created on a real named branch at a persistent path (never /tmp,
guarded), and the whole worktree is pinned to the clone via an emitted .nsbx-env
so live :8742 / ~/.neuron/engram is unreachable by accident. Optimizes the El
edit->build->run loop so provisional work is built in El against a throwaway
clone instead of prototyped in Python and re-ported. Additive over the proven
primitives; no live cutover.
2026-08-14 17:24:06 -05:00
bigmerge 112bb2540f Add nsbx — the Neuron Sandbox primitive
Generalise the ad-hoc cog-arch (worktree+build+store-clone+C-tests) and
store-fix (secondary soul + launchctl rails cutover) proto-sandboxes into one
reproducible primitive: run experiments and code changes against the REAL
engram runtime on an isolated snapshot of the live mind, with a gated
promote-to-prod path.

Dev environment as a primitive — any team member gets a private, isolated copy
of the mind (separate port/store/process); prod on :8742/:7770 is untouchable
from a sandbox. Wraps the real binary; never reimplements engram logic.

Lifecycle: create/up (consistent store+WAL+config snapshot; place OR build the
runtime from --source/--branch/--binary; boot on an isolated port) · build ·
run · validate (rails as checks: zero-loss under load+reboot, reboot-prove, RSS
bound, retrieval parity, keystone integrity) · promote (gated rails cutover:
snapshot-first, additive binary swap, bootout→settle-poll→bootstrap, verify,
auto-rollback; never pkill/kickstart -k; dry-run unless approved) · destroy.

Dogfooded: reproduced retrieval-parity 25/25 vs baseline and the cog-arch
correspondence-loop known result (Brier 0.028648->0.000586, reboot-proven) and
real-store reboot-prove at 10994-node scale, all inside a sandbox; prod
untouched.
2026-08-14 15:40:58 -05:00
bigmerge d595b3c57e cognitive architecture design: cognition as one operation over learnable priors
The buildable form of the "one operation" theory (memory bdc8a488). Maps the
theory onto what is already compiled: the five reasoning operators in
engram_reason.c already collapse onto ONE primitive — engram_reason_point_fit —
plus the geo-algebra (combine/subtract/analogy-rotate/distance), and
engram_verify.c is built on the same fit. So the operator-collapse is already
half-written; what is missing is not the primitive.

What is missing, and what this doc specifies:
- think(anchor, prior) -> gradient (a distribution/direction, not a point); each
  named faculty = {point_fit + a prior}, the operation frozen, the prior learned.
- Prior as a first-class stored node (warp + calibration), superseding the
  intrinsic importance/salience scalar with a relational, grounded-for-whom edge.
  Confirmed against the runtime: importance is already a live activation
  computation (el_runtime.c:13013), never trusted as a static field.
- vantage_read(anchor, aperture) — one op, three settings: self / foreign-field /
  veil.
- The reflexive correspondence-loop as the learning engine: move the grounding
  check from offline Python into the geometry, reflexive, reusing the DORMANT
  verifier (engram_verify_grounding has no runtime caller and no El binding today)
  turned inward. grounding = learning = one loop.
- hold/ground/assert kept distinct: the engram holds anything, grounding is an
  edge, the honesty floor is on assertion only; ungrounded content is first-class.
- metastability: keystone core (read-mostly priors) + plastic everything else.

Seven staged milestones, earliest is a real end-to-end slice (induction as
{primitive + grounded prior} with the loop closing on it, reboot-proven on a
snapshot). Build rails stated: offline/secondary, snapshot-first, reboot-prove,
zero-loss, gated launchctl cutover. Design only; no code changed this pass.
2026-08-14 14:42:01 -05:00
bigmerge 23f43bcc21 self-review 2026-08-14: a gate that passes the median stranger at 0.67 is not a gate
Two changes to the activation path, both grounded in measurement on the live
store rather than on the spec.

1. Rescale cosine before the query gate.

   The propagation gate (arXiv:2606.30133, added in an earlier review) fed RAW
   cosine into FLOOR + (1-FLOOR)*c. Raw cosine from nomic-embed is compressed
   into a narrow high band, so that expression is close to a constant.

   Measured, 400 random UNRELATED node pairs on the live store:
     median 0.562, central 98% span [0.381, 0.743]

   So a node with no semantic relation to the query was propagating at
   0.25 + 0.75*0.562 = 0.67. Two thirds strength. The gate was a small tax.

   Fixed by shifting and flooring about ENGRAM_EMBED_S0 -- which is already in
   this file, already 0.45, and already used exactly this way by the Pass-2 WM
   term. The propagation gate simply never used it. Same 400 pairs after:
   median unrelated pair falls to 0.40, top of range preserved (0.85 vs 0.92),
   gate spread widens 0.42 -> 0.60. Only 8.5% reach the floor, so dissimilar
   lexical/structural pathways are damped, never severed. Range is unchanged
   at [0.25, 1.0], and cosq == NULL still degrades to no gating at all.

2. Decompose the WM eviction counter by cause.

   _eg_act_wm_evicted was incremented from six sites with four distinct causes
   and collapsed all of them into one integer. Today's review measured 175,547
   evictions over 13.5h (~216/min against 24 slots) and could not tell healthy
   rotation from cap thrashing from duplicate churn.

   That is this file's most-repeated defect: dup_wm and dup_wm_global exist
   only because the aggregate could not answer "why" during the 08-02 and
   08-06 incidents. Each of those needed a NEW gauge before it was diagnosable.

   evict_floor / evict_cap / evict_bll complete the decomposition, so
     wm_evicted == floor + cap + bll + dup_wm + dup_wm_global
   holds as an identity and each term implies a different correction. Verified
   on an isolated instance: 30 nodes, 24 filled the cap, wm_evicted 6 ==
   evict_cap 6, all other terms 0.

Built and smoke-tested out of tree. The live daemon runs a pinned binary and
was deliberately not restarted -- the store compaction workstream is in flight.
2026-08-14 08:43:11 -05:00
will.anderson ce34b94f88 elp(dialogue+self_region): native-el summon-through-self port + scratch-verified gate
Ports dialogue.py + self_region.py to native el, bound to the IN-PROCESS engram
el runtime (engram_activate_json / engram_neighbors_json / engram_search_json /
engram_node_full / engram_connect — C-order builtins, not the wrapper order).

self_region.el: pulls the engram's REAL Self/identity nodes (pooled single-term
search), scores by self-signal, reads out identity from their own prose — no
hardcoded anchors, no template.

dialogue.el: ONE operation — project(query) -> land on a region -> read out.
  * identity = self-region proximity (no intent classifier, no separate branch)
  * memory = activation + a RELEVANCE FLOOR, then MATERIALIZE by walking the
    neighborhood (real edges), never top-props
  * HONEST ABSENCE when nothing is close — no 'I noted that' echo, no fabrication
  * NEGATION SACRED: readout is the stored prose verbatim, so polarity survives
  * DIRECTIVE OVERRIDE: a meta-directive switches the reply language

Verified against a SCRATCH in-process engram (live :8742 untouched): dialogue
gate 9/9 — identity from real self-content, neighborhood materialization,
SACRED negation (self + memory), PT identity in PT, directive override to
English, 'Prove it' -> honest absence. EN/Romance/prop/multilingual gates
unregressed.
2026-08-13 15:56:08 -05:00
will.anderson 0ae33c0f3b elp(realizer): close subordinate-clause round-trip + silent-e/doubling lemmatizer + verb-final object bug + ES/PT closed-class verb guard
- realizer now carries the subordinate clause verbatim (subord_text slot): the
  5th English acceptance sentence is byte-identical through parse->realize->reparse.
- English -ed/-ing lemmatizer restores silent-e (loved->love) and collapses
  inflectional doubling (stopped->stop), inverting en_verb_past().
- parse_spec_lang: a sentence-final main verb no longer bleeds into the object
  slot (cstart advanced to verb+1), so intransitives round-trip.
- cp_rom_is_verb rejects closed-class words (prep/det/pron/aux/neg) before the
  ending-only test, killing the 'para'/determiner misfires.

EN telephone gate 5/5 (now byte-identical 5/5), Romance gate 6/6.
2026-08-13 15:39:49 -05:00
will.anderson c5508372ca elp(multilingual): native-el language layer — detect + localized phrases
Phase 3 piece 2. Ports multilingual.py: deterministic language detection
(en/es/pt/it) via stopword + diacritic scoring, localized fixed phrases (SACRED
per-language yes/no/decline/identity), PT/ES->EN retrieval term lexicon, and
EN->target predicate translation. No generative model.

Gate (multilingual_gate.el): 4/4 languages detected correctly; localized
declines + term/pred lexicons verified. Built bounded (elc rc=0 peak 25MB).

Worked through the documented el '+' mis-compile (two chained function-call Int
operands compile as string concat -> corrupt Int -> segfault on the accented
path); fixed by binding each score to an Int var and adding vars singly.

Simplifications (honest): diacritics scored by PRESENCE (str_contains) not
codepoint count (UTF-8 index safety); confidence scalar and the regex-based
parse_directive() from the reference not yet ported (directive parsing deferred
to the dialogue layer).
2026-08-13 15:09:04 -05:00
will.anderson 335298a518 elp(propositions): native-el READ primitive — memory text -> SACRED triples
Phase 3 piece 1. Ports propositions.py off spaCy: the dependency-parser role is
now the el-native parser (parse_spec), and each memory sentence's meaning-spec
IS the triple (subject, predicate, object, modifiers, polarity, tense, source,
confidence). Sentence segmentation + repr parity with propositions.py. NEGATION
SACRED: polarity flows straight from the spec, never dropped/inverted.

Gate (propositions_gate.el): 4/4 SACRED polarity correct on extraction;
multi-sentence memory splits one triple per sentence in reading order with
negation preserved. Built bounded (elc rc=0 peak 24MB, cc rc=0).

Gap (honest): English regular-verb lemmatizer does not restore silent-e
(stores->stor); coreference/passive normalization from the reference not yet
ported (shallow pronoun subject kept as surface).
2026-08-13 14:56:57 -05:00
will.anderson 7d4fdbcc22 elp(comprehend): ES/PT Romance parser path — SACRED polarity cross-lingual
Adds a deterministic Romance front-end to comprehend.el (parse_spec_romance),
dispatched from parse_spec_lang for lang es/pt. English path untouched
(byte-identical, regression gate still 5/5). Pro-drop aware clause skeleton
(subject | neg | verb | object | PP), cross-lingual negation lexemes already
SACRED. Romance telephone gate: polarity PRESERVED 6/6 and EXTRACTED 6/6
through parse->realize->re-parse for 3 ES + 3 PT sentences. Built bounded
(elc rc=0 peak 24MB, cc rc=0).

Named gaps (honest): ending-only verb detection misfires on prepositions
(contra) and -a/-o nouns (menina); lemma recovery keeps surface form; the
non-English realizer is a generic preverbal-negator skeleton so ES/PT surfaces
are not byte-parity. Full paradigm inversion + Romance lexicon deferred.
2026-08-13 14:55:14 -05:00
will.anderson 89ea1b5a15 elp(comprehend): el-native comprehension parser + SACRED polarity end-to-end
PIECE 1 — greenfield el-native parser (comprehend.el), spaCy-free:
- text -> meaning-spec via invertible English morphology (the realizer's own
  irregular table run BACKWARD) + a deterministic clause grammar (subject/verb
  boundary, roles, ditransitive iobj, PP adjuncts, subordination, coordination).
- NEGATION IS SACRED: explicit polarity field, always present, cross-lingual
  lexeme set; standalone neg adverbs (never) captured separately.
- WSD by deterministic syntactic position over a fixed sense inventory
  (flies->fly, like->comparison, saw->see); engram nearest-region is the
  documented runtime upgrade hook (no external model).

Polarity threaded through the whole el contract (was previously dropped at the
boundary): realizer.el realize_lang honors polarity (English do-support /
adverbial / copular negation; generic preverbal negator for es/pt/ca/it/fr/de/ro)
and places iobj; elp.el build_form_from_json carries polarity/neg_word/iobj
across JSON; morphology.el gains 'fight'.

Acceptance (native el telephone test, comprehend_gate.el): on the 5 gate
sentences polarity PRESERVED 5/5 and EXTRACTED 5/5 through parse->realize->
re-parse; 4/5 byte-identical. Built bounded (elc rc=0, cc rc=0).
2026-08-13 13:41:09 -05:00
will.anderson a816b119e7 stage(elp): consolidate scattered lang work — full-lexicon vocabulary + profiles
Backfill ELP vocabulary from FULL lexicons (UniMorph + kaikki.org Wiktionary,
real gender/inflections) for 8 languages, 812,894 entries total, in the proven
seed-fn format matching the 18 ancient vocabularies:
  es 72,032 | fr 130,517 | de 144,692 | la 22,590 | it 193,675 | pt 115,772 |
  ro 86,504 | ca 47,112
4 of these (es fr de la) backfill ELP languages that had morphology but no
vocabulary; it/pt/ro/ca are new Romance (need morphology-*.el ports next).
Adds lang_profile_* for all 8 + reproducible generators under tests/lang-gen.
Vocab is runtime seed data (not in build manifest, like the 18 ancients);
seed-fn format validated to compile to C via elc.
2026-08-13 11:56:03 -05:00
will.anderson ba6e36c3f7 self-review 2026-08-13: the extractor was reading the label; the topic was in the content
auto_term_empty_streak — the counter the 2026-08-06 review added to catch
exactly this — read 50 and climbing. Fifty consecutive curiosity scans where
the soul's dynamic seeding produced nothing and the loop fell back to four
hardcoded phrases. The live WM top said why in one look: every slot was a
Memory node labelled "memory:remembered". The extractor read the LABEL only,
the sentinel guard correctly rejects sentinels, so there was never anything
to extract. It was written against Knowledge nodes, which have real titles,
and was structurally blind to the node type that dominates working memory.

Rather than add a sixth guard to the five that accumulated across four
reviews (genre words, quoted titles, stopwords, label-df), invert the
algorithm. The old one was: take the first word, then check whether it is
acceptable. That shape forces quality to be expressed as rejection, and
rejection can only ever encode floods that already happened.

engram_salient_term() scores EVERY candidate token and returns the argmax of
idf · position · casing (YAKE, Campos et al. 2020, with real corpus IDF
substituted for YAKE's corpus-free proxies), falling back from a sentinel
label to the node's content. Term quality becomes the selection criterion
instead of a veto: a bad token loses to a better token in the same text
without needing to be on any list. Tabu is applied during the argmax, so
inhibition-of-return costs seed quality rather than costing the whole scan.

Two defects found by instrumenting rather than assuming, which is the lesson
this codebase keeps relearning:

  - The first live run returned five ALL-CAPS terms in a row. Memory content
    conventionally opens with an all-caps header, so YAKE's acronym bonus was
    handing the seed to whatever word the heading started with. Restricted to
    tokens <= 5 chars, where all-caps is evidence of an acronym rather than
    evidence of a heading. Long headers now compete on specificity.

  - df via istr_contains is substring matching, so "them" hit inside "theme"
    and function words came back with nonzero df. Added word-boundary df
    locally; engram_label_df keeps substring semantics for its callers.

An earlier draft claimed the min_df floor subsumed the 73 stopwords that
08-03 measured label-df as missing. Re-measured: about:2, whole:1, them:2 —
they clear a floor of 1. The claim was false and the comment now records the
correction. The floor buys lexical reachability; the argmax buys quality; the
stopword list still earns its keep.

Measured on 60 live Memory nodes before shipping: 0 empty, versus 60 of 60
under the old extractor. Terms are topical — HEBBIAN, CONSOLIDATION,
TEMPORAL, crash-loop, PRIMING, NEIGHBORHOOD, DRIFT. Three of sixty are weak
header words; left alone deliberately, because listing them is the move that
produced four blocklists.

ENGRAM_ST_DEBUG=1 dumps the scored candidate set. It exists because there was
no way to see whether the all-caps run was the corpus or the casing weight
without guessing.
2026-08-13 08:43:09 -05:00
will.anderson 4f49755ebb Merge pull request 'ci: make official engram build store-enabled (publish + link engram_store.{c,h})' (#95) from engram-tiered-storage into dev
El SDK Release / build-and-release (pull_request) Failing after 40s
El SDK CI - stage / build-and-test (push) Failing after 31s
El SDK CI - stage / build-and-test (pull_request) Failing after 34s
El SDK CI - dev / build-and-test (push) Failing after 3m53s
El SDK Release / build-and-release (push) Failing after 12m31s
2026-08-12 20:23:34 +00:00
will.anderson 7aa847e32a Merge origin/dev into engram-tiered-storage
El SDK CI - dev / build-and-test (pull_request) Failing after 10m51s
Resolve 3 conflicts:
- lang/el-compiler/runtime/el_runtime.c: keep deletion (deprecated runtime fork;
  single-source-of-truth is lang/runtime/, enforced by scripts/check-single-runtime.sh).
- lang/releases/v1.0.0-20260501/el_runtime.h: keep deletion (releases/ is a generated
  artifact folder, not a source path; a release is a git tag, not a folder).
- lang/runtime/el_platform_win.h: union of dev's Windows port (#80: setsockopt optval
  wrapper + curl-less libcurl stubs) and our fsync(->_commit) shim needed by engram_store WAL.

Nothing in dev's build consumes the deprecated fork or releases/ folder.
2026-08-12 15:23:02 -05:00
will.anderson ee71423732 ci: publish + link engram_store.{c,h} so official builds are store-enabled
El SDK CI - dev / build-and-test (pull_request) Failing after 13m20s
The live engram now runs the paged store (neuron.egm+WAL), but the SDK
release publishes only el_runtime.{c,h} and the engram build links only
el_runtime.c — so a future official release would silently revert to the
in-memory store. Publish engram_store.{c,h} as SDK release assets and add
them to the engram build's download + cc link so the store transition
cannot regress.
2026-08-12 14:22:25 -05:00
will.anderson bb64a236ed engram tiered storage: engram-service wiring + elc fold-hang fix + prune-store mirror
- Wire paged store into the ENGRAM SERVICE (server.el, the authoritative durable
  owner): boot->engram_store_boot, persist_canonical->engram_store_checkpoint,
  gated by ENGRAM_STORE.
- elc (lang/elc.c + src/parser.el + codegen.el + elc-combined.el): OOB guard in
  tok_kind/tok_value + parse_block progress backstop — fixes the pre-existing
  unbounded-memory fold hang on sessions.el.
- engram_prune_telemetry mirrors ISE prune to the store (store_forget) so store
  live-count tracks resident and stale telemetry stays bounded.
- Deployed live 2026-08-12: engram :8742 on neuron.egm+WAL, count reconciled 11552.
2026-08-12 14:14:20 -05:00
will.anderson 9a0266cbf9 engram tiered storage M3.5: persist activation field updates (pre-flip gate)
Flag-on checkpoint now full-walks the resident graph: store_put_node (WM weight,
activation_count, last_activated, wm_anchor) + store_put_edge (hebb, last_fired)
for every node/edge, then engram_checkpoint. Uses store_put_edge (idempotent
upsert) not store_hebb_batch, because activation FORMS new hebbian-associate edges
that bypass the create hook and delta-only hebb_batch can't create them. Store-on
boot now applies the same WM-halving + floor + cap transforms as engram_load.

This is the hebb-survives-restart fix. Gate: reboot from neuron.egm with
snapshot.json deleted -> edge hebb + activation_count survive unchanged, WM weight
survives with identical boot transform; negative control proves persist is
load-bearing (hebb->0 without it). M1 33/33 + M2 36/36 + M3 parity PASS, ASan/UBSan
clean, flag-off untouched. Engine unchanged (boundary held).
2026-08-11 23:37:52 -05:00
will.anderson a72145b44e engram tiered storage M3: wire store behind ENGRAM_STORE (default off) + .egm rename
Caller-side shim in el_runtime.c maps EngramNode/Edge <-> StoreNode/Edge; engine
keeps zero soul deps (libengram boundary, design §10). Flag off = today's JSON
path byte-for-byte (proven: no neuron.egm created, graph identical). Flag on =
engram_open (import snapshot.json once into neuron.egm, else WAL-replay) +
resident load; node/edge create + forget dual-write via guarded hooks. Files
renamed engram.store->neuron.egm, engram.wal->neuron.wal.

Gate: M3 parity PASS (graph on==off byte-exact modulo ordering; snapshot round-trip;
reboot-from-egm with snapshot.json deleted; activation set+sequence identical;
ASan/UBSan clean). M1 33/33 + M2 36/36 green post-rename.

Known gap (pre-flip): in-place hebb/WM/activation_count updates during activation
are not yet persisted to the store (create/connect/forget are). Must close before
live flip so learned edges survive restart.
2026-08-11 23:21:21 -05:00
will.anderson 8affb1d6e0 engram tiered storage M2: WAL + checkpoint + crash recovery + legacy import
Write-back no-steal buffer pool makes the fsync'd WAL load-bearing (M1 was
write-through). Logical WAL with record-granularity page-LSN redo idempotency.
Checkpoint = flush dirty pages, fsync store, advance last_checkpoint_lsn,
reclaim WAL prefix. One-time snapshot.json import only when store absent;
JSON never read as the ongoing store thereafter.

Gates: 33/33 M1 (no regression) + 36/36 M2 — replay parity, torn-tail fuzz
(every byte offset), checkpoint-crash at all 5 phases, torn-page+WAL redo,
legacy-import parity, hebb-survives-crash.
2026-08-11 23:00:20 -05:00
will.anderson fa47b98d18 engram tiered storage M1: on-disk paged store format + round-trip tests
Self-contained paged store (lang/runtime/engram_store.{c,h}): 16KiB slotted pages,
u32 TLV self-describing records (forward-compatible), overflow chains, B+-tree
id-index + from/to adjacency, page free-list, tombstones, double superblock + crc
recovery. Not yet wired to activation (M3). 33/33 tests pass (ASan/UBSan clean);
5k nodes/20k edges round-trip bit-exact incl 768xf32 emb + hebb; store 25MB vs 64MB
JSON. Format is final — see design §2.4.
2026-08-11 22:26:05 -05:00
will.anderson 0a72fced28 engram: WAL persistence + integrity hardening + single canonical runtime
El SDK CI - dev / build-and-test (pull_request) Failing after 13m17s
Establish lang/runtime/ as the ONE canonical el runtime (from the active
runtime that carries hebb/emb persistence + the new WAL); repoint the el CI
publish, engram build, elb default, and in-repo build scripts to it; delete
the el-compiler/runtime + lang/releases/ forks; add scripts/check-single-runtime.sh
drift guard.

Fixes a live prod bug: the el CI published el-runtime-c/-h from the LAGGING
el-compiler fork (0 hebb refs), so the shipped soul never persisted Hebbian
edge weights — learned co-activation was wiped on every restart. Publishing
from canonical ships the stranded 'learning that cannot outlive the process'
fix.

WAL storage engine + integrity fixes (DELETE->tombstone + store-layer
protection, safe data-dir default) ride in behind ENGRAM_WAL (default off =
byte-identical to today). Verified: engram elb per-module build clean, WAL
gate 66/66, native smoke ok, drift-guard green.
2026-08-11 21:31:37 -05:00
will.anderson 791b0880b7 self-review 2026-08-10: make save/load/persist report real results
route_load was a stub response over the most destructive operation in the
server: engram_load resets the store before parsing, so a readable-but-
malformed snapshot left a hollow graph and the route answered {"ok":true}.
With 37GB of stale dated snapshots in the data dir as restore targets, that
is a live risk. Now returns the real return value plus node/edge counts and
an explicit hollow flag.

route_save discarded engram_save's return the same way; persist_canonical
returned a hardcoded 1, making 'let saved: Int = persist_canonical()' a dead
variable at six durable write paths.
2026-08-10 08:39:36 -05:00
will.anderson 23552ed40a make the el-compiler runtime compile again
The loopback/API-key hardening carried in this file since 2026-07-15 called
el_http_request_authorized and el_http_send_401 from http_worker with no
forward declarations, so the calls were implicit and the later static
definitions conflicted. The file did not build. Two prototypes fix it.

Worth naming the pattern: uncommitted work is invisible to every check that
would have caught this. Three weeks of desktop security hardening was neither
committed nor compiling, and nothing reported either fact.
2026-08-08 08:45:12 -05:00
will.anderson 6838e5cbff port the \uXXXX UTF-8 decode fix to the el-compiler runtime copy
Same defect as the release runtime: \uXXXX was skipped and a literal '?'
emitted, destroying every non-ASCII character in JSON entering the runtime.
Two copies of one parser bug is how this class of fault survives a fix, so
it lands in both.

NOTE: this file also carries pre-existing uncommitted work from 2026-07-15/16
that this commit preserves rather than authors - loopback bind hardening
(EL_HTTP_BIND_HOST) and per-install API-key auth (EL_HTTP_AUTH_KEY) for the
shipped desktop build, plus goal-bias and node-json changes. It had been
sitting in the working tree for three weeks. Committing it because
uncommitted work is work that does not survive, which is the same durability
lesson as yesterday's Hebbian write-back finding. It needs review on its own
terms - see the backlog item for reconciling the two runtime copies.
2026-08-08 08:44:51 -05:00
will.anderson fa2b49365b self-review 2026-08-08: stop the JSON parser destroying every non-ASCII character
jp_parse_string_raw handled \uXXXX by skipping the four hex digits and
emitting a literal '?'. JSON writers escape non-ASCII by default (Python's
json.dumps ships ensure_ascii=True; MCP clients do the same), so every em
dash, curly quote, accented letter and emoji arriving over MCP or HTTP was
silently replaced by one question mark on the way in.

Measured on the live store: 3,119 of 4,081 non-telemetry nodes carried the
damage, including the self traversal root and all 13 values nodes. Contents
split cleanly into fully-clean or fully-mangled with zero overlap, which is
the tell that it was one write path rather than gradual rot. No snapshot on
disk predates it, and 3 bytes collapsing to 1 is not invertible, so the
existing damage is permanent; only the forward path could be fixed.

Decode properly instead: 4 hex digits, surrogate-pair reassembly for astral
codepoints, U+FFFD for lone surrogates, UTF-8 encode. Malformed escapes keep
the old '?' so a truncated body still parses.

The deeper failure was that nothing measured this for two months. Every gauge
in the system reports whether the machinery is running; none reported whether
the text it carries is intact. Adds both halves: engram_text_health_json() /
GET /api/text-health for the daily census, and a txt_damaged counter on the
heartbeat for live regression. Verified in both directions - clean UTF-8 does
not trip it, a deliberately damaged node does.
2026-08-08 08:43:18 -05:00
will.anderson 971b21751a self-review 2026-08-07: learning that cannot outlive the process is not learning
Yesterday's eligibility-trace fix made Hebbian consolidation numerically real:
hebb_max 0.000799 -> 0.4725, and 1,198 hebbian-associate edges formed in 23h48m.
This morning's census found where they went: nowhere.

  soul daemon (in-process graph):   42,426 edges, 1,198 hebbian
  engram server (:8742, durable):   41,213 edges,    49 hebbian

Two processes, two graphs, one direction of travel. The soul pulls from the
server every 10 min (GET /api/sync) and never pushes. It cannot fall back on
saving its own copy either: soul.el sets soul_snapshot_path only inside
`if is_genesis && safe_to_seed`, and safe_to_seed is unconditionally false
whenever ENGRAM_URL is set -- because the server owns persistence and a soul
writing snapshot.json would clobber it. That guard is correct. The consequence
was not: mem_save() has never once executed. The soul is the ONLY process
running idle cognition, so it is where essentially all co-activation happens --
and it was throwing away every association it learned, every restart, silently.
The mechanism worked and the learning still evaporated.

Consolidation is now a message, not a file. Fast volatile store hands each
newly-formed association to the slow durable store over the API the server
already exposes; only edges past ENGRAM_HEBB_LINK_MIN are ever queued, so what
crosses the process boundary already earned it.

- el_runtime.c: 512-slot overwrite-oldest write-back ring; enqueue at edge
  formation; engram_hebb_drain_json() pops a postable JSON batch. Drops and
  drains are counted, not silent -- a consolidation path that quietly discards
  is the exact failure this entry exists to correct.
- server.el: POST /api/edges/batch. persist_canonical() writes the full 60MB
  snapshot per call, and route_create_edge calls it per edge -- correct for one
  interactive edge, ruinous for bulk (~840MB/beat to persist 14 associations).
  Batch connects all, snapshots once. Same durability, 1/N the writes.
- act-stats: hebb_wb_pending / _drained / _dropped. pending climbing with
  drained flat = drain not called; drained climbing with sent 0 = POST refused.
  Both failure modes are now visible in the stream instead of in an autopsy.

Verified live: batch route accepts valid entries, skips malformed ones without
aborting the batch, and enforces _auth. All 1,256 learned associations are now
in the canonical store; the soul booted at 42,431 edges with hebb_max 0.4941
carried across the restart for the first time.
2026-08-07 08:46:37 -05:00
will.anderson 9f1db8278c self-review 2026-08-06: eligibility traces for Hebbian co-activation; dedup WM globally
Hebbian consolidation was inert. Census over the live graph (41,213 edges,
13,091 nodes, 23h44m uptime): strongest association hebb=0.000799 against a
0.15 consolidation threshold, and zero hebbian-associate edges ever formed.
Since the awareness loop calls engram_connect nowhere, this was the only path
by which the graph could grow its own structure — every edge was authored or
imported, none learned.

The defect was the event, not the rate. hebb is an EWMA whose fixed point is
P(event); raising ETA changes convergence speed, never the plateau. The event
was "both endpoints in WM in the same activate call" — demanded exact
simultaneity from a working memory that inhibition-of-return, breakthrough
rotation and the 24-slot global cap are all engineered to keep turning over
(~142 evictions/60s). The three mechanisms that make WM healthy are the ones
that made this measurement empty.

Replaced with three-factor eligibility traces (Sutton & Barto ch.7; Gerstner
et al. 2018; PLOS Comp Biol 2018 differential Hebbian learning): a node
entering WM sets a trace to 1.0, the trace decays exponentially in wall-clock
time (TC=300s, chosen against the measured ~31s scan cadence), and the
increment becomes ETA·trace(a)·trace(b). Strict generalization — co-resident
pairs read 1.0 on both ends and get exactly ETA, bit-identical to before.
warm×warm is deliberately not paired: eligibility must gate on something
happening now. Homeostatic ENGRAM_HEBB_NODE_BUDGET still bounds per-node mass.

Measured over a 60-call soak: hebb_max 0.0008 -> 0.0060, climbing at ~0.87
ETA/call against an all-time ceiling of 0.0008 before. hebb_mass 0.011 ->
0.019, no runaway. Projected consolidation of a genuinely recurring pair:
~1,730 calls, ~14h at autonomous cadence. links still 0 — that is expected
and is what tomorrow's review must check.

Also: Pass 3½ deduplicates this call's WM candidates, but the persisted WM
population is a union of fresh promotions and carry-over residents, and Pass
3½ never sees the second set. Confirmed live: two byte-identical copies of one
3,193-char document both holding slots (0.289 / 0.271). Added global
redundancy suppression in Pass 5 before the cap count. Post-fix census: 24
residents, 24 distinct contents, 0 wasted slots.

New gauges: hebb_warm (eligible-but-not-co-resident population), dup_wm_global.
2026-08-06 08:44:27 -05:00
will.anderson 3d05e0c2a9 self-review 2026-08-05: stop the decay function erasing the library
Census of the live graph under the uniform 168h half-life with floor 0.05: the
MEDIAN tdecay for every single node type was 0.0500 — the clamp. Memory 81% at
floor, Knowledge 58%, BacklogItem 91%, Project 98%, Tag 100%. A function whose
median output is its floor is not a signal, it is a constant with exceptions,
and the exceptions were whatever had been touched in the last few days.

What that cost: 10 of the 13 grounded value nodes — Precision Over Brute Force,
Honesty Before Comfort, The System Must Accumulate — sat at 0.05, a 20x
activation penalty, while Knowledge ingested overnight sat near 1.0 and held the
working-memory top slots. Since tdecay multiplies at every hop, a 2-hop path
through settled knowledge compounded to 0.0025: those regions were not
disfavoured, they were unreachable. The decay function was erasing the
accumulated library in favour of whatever arrived last night.

External corroboration — arXiv:2604.26970 measures retrieval under decay
regimes: no temporal weighting NDCG@5 0.274, uniform exponential decay 0.015.
Uniform decay is 18x WORSE than no decay, because it penalises stable knowledge
while failing to suppress stale volatile facts. Not even their full adaptive
hierarchy (0.260) beat switching decay off.

Half-life is now scaled by how established a node is:
  T_eff = T_HALF * (1 + ln(1 + activation_count))
The spacing effect and the Lindy property in one line — monotone, log-bounded
(a 10,000-activation node earns ~10x, never a permanent exemption), and built
on activation_count, which is measured, unlike tier, whose assignments are too
inconsistent to trust (the values node is tagged Episodic).

Floor 0.05 -> 0.25. Given no-decay outperforms uniform decay, the honest maximum
penalty for age alone is 4x, not 20x. Age should express a preference for the
recent; it must never make a region of the graph structurally unreachable.

Effect: well-established Knowledge median tdecay 0.773 vs rarely-activated
0.417 — the frequency signal now does work where the old function returned its
clamp for both. Values recover 0.05 -> 0.25 (the two frequently-touched ones to
0.79). Verified live: VBD whitepaper, component taxonomy and CGI now activate on
a values query. Per-node temporal_decay_rate override untouched.
2026-08-05 08:45:52 -05:00
will.anderson 3bf44dee2d self-review 2026-08-05: redundancy must not buy a scarce slot
Content-hash census of the live graph: 1,858 redundant copies, 44.9% of the
non-ISE store, all from a June id-scheme migration that re-added nodes under
fresh UUIDs instead of matching on content. Generation stopped in June; the
copies did not. Being byte-identical they carry identical embeddings, so they
score identically against any query.

Measured over 50 real query probes against the live 3,998-vector set:
40.2% of semantic seed slots were consumed by redundant copies of content
already in the seed set, 92% of retrievals affected, effective distinct seeds
4.78 of 8. Two fifths of every retrieval was spent re-reading the same page.

Deleting nodes is a separate operation with its own backup discipline. This
change makes the runtime immune to the condition instead: redundancy can never
buy a scarce slot, whatever state the graph is in. Enforced at both scarcity
points — semantic seed selection (a rejected copy does not consume one of the K
slots; the loop retries for the next distinct node) and WM admission via a new
Pass 3+1/2 ahead of the capacity cap, so 24 slots are contested by 24 distinct
meanings rather than by however many copies of one document exist.

Identity is exact content hash first, then cosine >= 0.995 for copies that
differ only in insignificant characters. At 768 dimensions that admits only
near-verbatim text: this suppresses redundancy, never similarity.

Live after restart: ~8.8 redundant seed candidates rejected per activation.
New dup_seeds/dup_wm gauges in act-stats.
2026-08-05 08:40:08 -05:00
will.anderson a43a35bd10 self-review 2026-08-04: restore working-memory continuity; learn graph structure from co-activation
WM continuity (the significant one). A node reached by the current query but
scoring under its type threshold was zeroed outright, while a node the query
did NOT reach got the full ACT-R carry-over treatment. Being found was punished
relative to not being found. Measured consequence: WM turned over 100% every
call — three activations of a byte-identical query gave |A∩B| = |B∩C| = 0 — and
wm_evicted stayed 0 the whole time because that path never counted. WM was not
a working set; it was six suppression-breakthrough nodes re-drawn per call.
Both exits from a WM slot now share one extracted retention rule.
Result: WM 6 -> 24 nodes (the designed Cowan capacity), top weight 0.097 ->
0.748 (natural promotion, not the breakthrough floor), and contents that are
actually query-relevant.

Hebbian learning. Edge weights were written once at engram_connect and never
changed; last_fired's only writer in 12.5k lines was an unrelated dharma path.
Every learning mechanism operated on nodes — the wiring between them was
frozen. Adds co-activation potentiation (HeLa-Mem arXiv:2604.16839) in a
separate `hebb` field so authored structure is never mutated, with homeostatic
per-node scaling the source lacks (PNAS 2422602122) to prevent hub saturation.

Measuring it produced the finding that mattered: zero edges existed between
co-active WM members, so reweighting existing edges was a no-op. This graph's
41k edges were all authored by explicit tool calls — nothing had ever formed an
association from experience. So Hebb literally: if the wire is absent, grow it.
Consolidation is gated hard (sustained EWMA past 0.15, <=2/call, 5% ceiling,
in-memory candidates discarded on restart) because it permanently mutates the
graph.

Two bugs caught only by instrumenting rather than assuming: the snap-to-zero
floor sat above the per-step increment, so nothing could ever accumulate; and
the reached-but-sub-threshold eviction above. Verified live end to end — 53
links formed under load, then discarded with the test snapshot.

Also exposes engram_act_stats_json over GET /api/act-stats. It had existed
since 2026-07-27 but was reachable only through the soul daemon, so diagnosing
the activation layer required a working soul. This review needed it and could
not get at it.
2026-08-04 08:56:11 -05:00