Compare commits

..

1 Commits

Author SHA1 Message Date
will.anderson 5baab050e2 ci: test pr->dev hook
El SDK CI - dev / build-and-test (pull_request) Failing after 30s
2026-05-06 17:19:56 -05:00
397 changed files with 19412 additions and 79046 deletions
+63 -176
View File
@@ -22,46 +22,39 @@ jobs:
- name: Install build dependencies
run: |
apt-get update -qq
apt-get install -y gcc libcurl4-openssl-dev apt-transport-https ca-certificates
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" \
> /etc/apt/sources.list.d/google-cloud-sdk.list
apt-get update -qq && apt-get install -y google-cloud-cli
apt-get install -y gcc libcurl4-openssl-dev
# Seed: use the committed linux-amd64 binary as the bootstrap
- name: Bootstrap from committed linux binary (seed)
# Gen2: compile the bootstrap C source into a working elc binary
- name: Build elc from bootstrap (gen2)
run: |
chmod +x dist/platform/elc-linux-amd64
echo "seed elc (committed linux-amd64 binary)"
dist/platform/elc-linux-amd64 --version || true
# Gen2: use seed to self-host compile the El compiler
- name: Self-host compile El compiler (gen2)
run: |
dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c
# -Wl,--allow-multiple-definition: elc-bootstrap.c and el_runtime.c both define
# is_digit/is_whitespace; bootstrap predates the text-processing primitives commit
gcc -O2 \
-I runtime \
dist/elc-gen2.c \
runtime/el_runtime.c \
-lcurl -lssl -lcrypto -lpthread -lm \
-I el-compiler/runtime \
dist/elc-bootstrap.c \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-Wl,--allow-multiple-definition \
-o dist/elc-gen2
chmod +x dist/elc-gen2
echo "gen2 elc built"
dist/elc-gen2 --version || true
# Gen3: use gen2 to compile the El compiler from its own El source (self-host)
- name: Self-host compile El compiler with gen2 (gen3)
run: |
mkdir -p dist/platform
dist/elc-gen2 el-compiler/src/compiler.el > dist/elc-gen3.c
gcc -O2 \
-I el-compiler/runtime \
dist/elc-gen3.c \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-o dist/platform/elc
chmod +x dist/platform/elc
echo "gen2 (self-hosted) elc built"
echo "gen3 (self-hosted) elc built"
dist/platform/elc --version || true
# Build elb (needed for Artifact Registry publish and downstream CI)
- name: Build elb
run: |
mkdir -p dist/bin
dist/platform/elc elb.el > dist/elb.c
gcc -O2 \
-I runtime \
dist/elb.c \
runtime/el_runtime.c \
-lcurl -lssl -lcrypto -lpthread -lm \
-o dist/bin/elb
chmod +x dist/bin/elb
echo "elb built"
- name: Run tests - text
run: |
ELC="$(pwd)/dist/platform/elc" \
@@ -87,153 +80,117 @@ jobs:
bash tests/html_sanitizer/run.sh
# Native El test suites (elc --test, compile-link-run)
# el_runtime.c is precompiled to .o once and reused by all 8 modules.
- name: Precompile el_runtime.o
run: |
set -euo pipefail
RUNTIME="$(pwd)/runtime"
gcc -O2 -c -I "$RUNTIME" "$RUNTIME/el_runtime.c" \
-o /tmp/el_runtime.o
echo "el_runtime.o compiled"
- name: Run tests - native (core)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_core
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_core
/tmp/el_native_core
- name: Run tests - native (text)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_text
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_text
/tmp/el_native_text
- name: Run tests - native (string)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_string
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_string
/tmp/el_native_string
- name: Run tests - native (math)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_math
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_math
/tmp/el_native_math
- name: Run tests - native (state)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_state
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_state
/tmp/el_native_state
- name: Run tests - native (time)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_time
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_time
/tmp/el_native_time
- name: Run tests - native (json)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_json
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_json
/tmp/el_native_json
- name: Run tests - native (env)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_env
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_env
/tmp/el_native_env
- name: Run tests - native (fs)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c /tmp/el_runtime.o \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_fs
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_fs
/tmp/el_native_fs
# Build epm binary using elb (epm lives at repo root, not inside lang/)
- name: Build epm
run: |
ABS_ELB="$(pwd)/dist/bin/elb"
ABS_ELC="$(pwd)/dist/platform/elc"
ABS_RUNTIME="$(pwd)/runtime"
ABS_OUT="$(pwd)/dist/bin"
(cd ../epm && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
chmod +x dist/bin/epm
echo "epm built"
# Build el-install binary using elb
- name: Build el-install
run: |
ABS_ELB="$(pwd)/dist/bin/elb"
ABS_ELC="$(pwd)/dist/platform/elc"
ABS_RUNTIME="$(pwd)/runtime"
ABS_OUT="$(pwd)/dist/bin"
(cd tools/install && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
chmod +x dist/bin/el-install
echo "el-install built"
# Publish only after merge (push event), not on PR validation runs
- name: Publish El SDK to Artifact Registry (dev)
if: github.event_name == 'push'
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
run: |
# Fail loudly: previously this step had no `set -e`, so an auth or
# upload failure was swallowed (step exited 0 on the trailing echo)
# and the SDK silently never published. Surface failures now.
set -euo pipefail
if [ -z "${GCP_SA_KEY:-}" ]; then
echo "FATAL: GCP_SA_KEY secret is empty — cannot authenticate to publish" >&2
exit 1
fi
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
apt-get install -y -qq apt-transport-https ca-certificates gnupg curl
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
apt-get update -qq && apt-get install -y google-cloud-cli
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project neuron-785695
echo "Publishing as active account: $(gcloud config get-value account 2>/dev/null)"
VERSION="${GITHUB_SHA:0:8}"
VERSION="${GITEA_SHA:0:8}"
gcloud artifacts generic upload \
--repository=foundation-dev \
--location=us-central1 \
--project=neuron-785695 \
--package=el-elc \
--package=el/elc \
--version="${VERSION}" \
--source=dist/platform/elc
@@ -241,87 +198,17 @@ jobs:
--repository=foundation-dev \
--location=us-central1 \
--project=neuron-785695 \
--package=el-elb \
--package=el/el_runtime.c \
--version="${VERSION}" \
--source=dist/bin/elb
--source=el-compiler/runtime/el_runtime.c
gcloud artifacts generic upload \
--repository=foundation-dev \
--location=us-central1 \
--project=neuron-785695 \
--package=el-runtime-c \
--package=el/el_runtime.h \
--version="${VERSION}" \
--source=runtime/el_runtime.c
gcloud artifacts generic upload \
--repository=foundation-dev \
--location=us-central1 \
--project=neuron-785695 \
--package=el-runtime-h \
--version="${VERSION}" \
--source=runtime/el_runtime.h
gcloud artifacts generic upload \
--repository=foundation-dev \
--location=us-central1 \
--project=neuron-785695 \
--package=el-runtime-js \
--version="${VERSION}" \
--source=runtime/el_runtime.js
--source=el-compiler/runtime/el_runtime.h
echo "Published El SDK version=${VERSION} to foundation-dev"
# Keep key alive for the ci-base rebuild step below
# (deleted in that step after docker push)
- name: Rebuild ci-base with fresh El SDK (dev)
# Patches ci-base:dev in-place: pulls the existing image (which has all
# system deps — Node, Go, gcloud, Docker CLI, etc.) and overlays the freshly
# built El SDK on top. Keeps the full ci-base rebuild fast and incremental.
#
# continue-on-error: this is a CI-cache optimization, NOT the release
# artifact. It runs Docker (pull/build/push ~600MB) on the host-mode GCE
# runner where DinD/Docker availability is fragile. A failure here must
# never block or redden the job — the SDK publish above is the deliverable.
continue-on-error: true
if: github.event_name == 'push'
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
run: |
set -euo pipefail
CI_BASE="us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base"
SHA="${GITHUB_SHA:0:8}"
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project neuron-785695
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
# Pull existing ci-base:dev (or fall back to :latest on first run)
BASE_TAG="dev"
docker pull "${CI_BASE}:dev" || { docker pull "${CI_BASE}:latest" && BASE_TAG="latest"; }
# Inline Dockerfile — only replaces the El SDK layer
cat > /tmp/Dockerfile.ci-base-patch << 'EOF'
ARG BASE
FROM ${BASE}
COPY dist/platform/elc /opt/el/dist/platform/elc
COPY dist/bin/elb /opt/el/dist/bin/elb
COPY runtime/el_runtime.c /opt/el/runtime/el_runtime.c
COPY runtime/el_runtime.h /opt/el/runtime/el_runtime.h
COPY runtime/el_runtime.js /opt/el/runtime/el_runtime.js
RUN chmod +x /opt/el/dist/platform/elc /opt/el/dist/bin/elb
EOF
docker build \
--build-arg BASE="${CI_BASE}:${BASE_TAG}" \
--build-arg BUILDKIT_INLINE_CACHE=1 \
-f /tmp/Dockerfile.ci-base-patch \
-t "${CI_BASE}:dev" \
-t "${CI_BASE}:dev-${SHA}" \
.
docker push "${CI_BASE}:dev"
docker push "${CI_BASE}:dev-${SHA}"
echo "ci-base rebuilt: ${CI_BASE}:dev (${SHA})"
rm -f /tmp/gcp-key.json
+52 -139
View File
@@ -34,25 +34,35 @@ jobs:
apt-get update -qq
apt-get install -y gcc libcurl4-openssl-dev
# Seed: use the committed linux-amd64 binary as the bootstrap
- name: Bootstrap from committed linux binary (seed)
# Gen2: compile the bootstrap C source into a working elc binary
- name: Build elc from bootstrap (gen2)
run: |
chmod +x dist/platform/elc-linux-amd64
echo "seed elc (committed linux-amd64 binary)"
dist/platform/elc-linux-amd64 --version || true
# Gen2: use seed to self-host compile the El compiler
- name: Self-host compile El compiler (gen2)
run: |
dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c
# -Wl,--allow-multiple-definition: elc-bootstrap.c and el_runtime.c both define
# is_digit/is_whitespace; bootstrap predates the text-processing primitives commit
gcc -O2 \
-I runtime \
dist/elc-gen2.c \
runtime/el_runtime.c \
-lcurl -lssl -lcrypto -lpthread -lm \
-I el-compiler/runtime \
dist/elc-bootstrap.c \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-Wl,--allow-multiple-definition \
-o dist/elc-gen2
chmod +x dist/elc-gen2
echo "gen2 elc built"
dist/elc-gen2 --version || true
# Gen3: use gen2 to compile the El compiler from its own El source (self-host)
- name: Self-host compile El compiler with gen2 (gen3)
run: |
mkdir -p dist/platform
dist/elc-gen2 el-compiler/src/compiler.el > dist/elc-gen3.c
gcc -O2 \
-I el-compiler/runtime \
dist/elc-gen3.c \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-o dist/platform/elc
chmod +x dist/platform/elc
echo "gen2 (self-hosted) elc built"
echo "gen3 (self-hosted) elc built"
dist/platform/elc --version || true
- name: Run tests - text
@@ -84,157 +94,113 @@ jobs:
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_core
-lcurl -lpthread -lm -o /tmp/el_native_core
/tmp/el_native_core
- name: Run tests - native (text)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_text
-lcurl -lpthread -lm -o /tmp/el_native_text
/tmp/el_native_text
- name: Run tests - native (string)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_string
-lcurl -lpthread -lm -o /tmp/el_native_string
/tmp/el_native_string
- name: Run tests - native (math)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_math
-lcurl -lpthread -lm -o /tmp/el_native_math
/tmp/el_native_math
- name: Run tests - native (state)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_state
-lcurl -lpthread -lm -o /tmp/el_native_state
/tmp/el_native_state
- name: Run tests - native (time)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_time
-lcurl -lpthread -lm -o /tmp/el_native_time
/tmp/el_native_time
- name: Run tests - native (json)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_json
-lcurl -lpthread -lm -o /tmp/el_native_json
/tmp/el_native_json
- name: Run tests - native (env)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_env
-lcurl -lpthread -lm -o /tmp/el_native_env
/tmp/el_native_env
- name: Run tests - native (fs)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_fs
-lcurl -lpthread -lm -o /tmp/el_native_fs
/tmp/el_native_fs
# Build elb (needed for epm and el-install builds below)
- name: Build elb
run: |
mkdir -p dist/bin
dist/platform/elc elb.el > dist/elb.c
gcc -O2 \
-I runtime \
dist/elb.c \
runtime/el_runtime.c \
-lcurl -lssl -lcrypto -lpthread -lm \
-o dist/bin/elb
chmod +x dist/bin/elb
echo "elb built"
# Build epm binary using elb (epm lives at repo root, not inside lang/)
- name: Build epm
run: |
ABS_ELB="$(pwd)/dist/bin/elb"
ABS_ELC="$(pwd)/dist/platform/elc"
ABS_RUNTIME="$(pwd)/runtime"
ABS_OUT="$(pwd)/dist/bin"
(cd ../epm && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
chmod +x dist/bin/epm
echo "epm built"
# Build el-install binary using elb
- name: Build el-install
run: |
ABS_ELB="$(pwd)/dist/bin/elb"
ABS_ELC="$(pwd)/dist/platform/elc"
ABS_RUNTIME="$(pwd)/runtime"
ABS_OUT="$(pwd)/dist/bin"
(cd tools/install && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
chmod +x dist/bin/el-install
echo "el-install built"
# Publish only after merge (push event), not on PR validation runs
- name: Publish El SDK to Artifact Registry (stage)
if: github.event_name == 'push'
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
run: |
# Fail loudly: previously this step had no `set -e`, so an auth or
# upload failure was swallowed (step exited 0 on the trailing echo)
# and the SDK silently never published. Surface failures now.
set -euo pipefail
if [ -z "${GCP_SA_KEY:-}" ]; then
echo "FATAL: GCP_SA_KEY secret is empty — cannot authenticate to publish" >&2
exit 1
fi
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
apt-get install -y -qq apt-transport-https ca-certificates curl
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
apt-get install -y -qq apt-transport-https ca-certificates gnupg curl
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
apt-get update -qq && apt-get install -y google-cloud-cli
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project neuron-785695
echo "Publishing as active account: $(gcloud config get-value account 2>/dev/null)"
VERSION="${GITHUB_SHA:0:8}"
VERSION="${GITEA_SHA:0:8}"
gcloud artifacts generic upload \
--repository=foundation-stage \
--location=us-central1 \
--project=neuron-785695 \
--package=el-elc \
--package=el/elc \
--version="${VERSION}" \
--source=dist/platform/elc
@@ -242,70 +208,17 @@ jobs:
--repository=foundation-stage \
--location=us-central1 \
--project=neuron-785695 \
--package=el-runtime-c \
--package=el/el_runtime.c \
--version="${VERSION}" \
--source=runtime/el_runtime.c
--source=el-compiler/runtime/el_runtime.c
gcloud artifacts generic upload \
--repository=foundation-stage \
--location=us-central1 \
--project=neuron-785695 \
--package=el-runtime-h \
--package=el/el_runtime.h \
--version="${VERSION}" \
--source=runtime/el_runtime.h
--source=el-compiler/runtime/el_runtime.h
echo "Published El SDK version=${VERSION} to foundation-stage"
# Keep key alive for the ci-base rebuild step below
# (deleted in that step after docker push)
- name: Rebuild ci-base with fresh El SDK (stage)
# Patches ci-base:stage in-place: pulls the existing image (which has all
# system deps — Node, Go, gcloud, Docker CLI, etc.) and overlays the freshly
# built El SDK on top. Keeps the full ci-base rebuild fast and incremental.
#
# continue-on-error: this is a CI-cache optimization, NOT the release
# artifact. It runs Docker (pull/build/push ~600MB) on the host-mode GCE
# runner where DinD/Docker availability is fragile. A failure here must
# never block or redden the job — the SDK publish above is the deliverable.
continue-on-error: true
if: github.event_name == 'push'
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
run: |
set -euo pipefail
CI_BASE="us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base"
SHA="${GITHUB_SHA:0:8}"
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project neuron-785695
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
# Pull existing ci-base:stage (system deps stay cached in the base layer)
docker pull "${CI_BASE}:stage" || docker pull "${CI_BASE}:latest"
# Inline Dockerfile — only replaces the El SDK layer
cat > /tmp/Dockerfile.ci-base-patch << 'EOF'
ARG BASE
FROM ${BASE}
COPY dist/platform/elc /opt/el/dist/platform/elc
COPY dist/bin/elb /opt/el/dist/bin/elb
COPY runtime/el_runtime.c /opt/el/runtime/el_runtime.c
COPY runtime/el_runtime.h /opt/el/runtime/el_runtime.h
COPY runtime/el_runtime.js /opt/el/runtime/el_runtime.js
RUN chmod +x /opt/el/dist/platform/elc /opt/el/dist/bin/elb
EOF
docker build \
--build-arg BASE="${CI_BASE}:stage" \
--build-arg BUILDKIT_INLINE_CACHE=1 \
-f /tmp/Dockerfile.ci-base-patch \
-t "${CI_BASE}:stage" \
-t "${CI_BASE}:stage-${SHA}" \
.
docker push "${CI_BASE}:stage"
docker push "${CI_BASE}:stage-${SHA}"
echo "ci-base rebuilt: ${CI_BASE}:stage (${SHA})"
rm -f /tmp/gcp-key.json
+75 -143
View File
@@ -34,26 +34,35 @@ jobs:
apt-get update -qq
apt-get install -y gcc libcurl4-openssl-dev
# Seed: use the committed linux-amd64 binary as the bootstrap
- name: Bootstrap from committed linux binary (seed)
# Gen2: compile the bootstrap C source into a working elc binary
- name: Build elc from bootstrap (gen2)
run: |
chmod +x dist/platform/elc-linux-amd64
echo "seed elc (committed linux-amd64 binary)"
dist/platform/elc-linux-amd64 --version || true
# -Wl,--allow-multiple-definition: elc-bootstrap.c and el_runtime.c both define
# is_digit/is_whitespace; bootstrap predates the text-processing primitives commit
gcc -O2 \
-I el-compiler/runtime \
dist/elc-bootstrap.c \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-Wl,--allow-multiple-definition \
-o dist/elc-gen2
chmod +x dist/elc-gen2
echo "gen2 elc built"
dist/elc-gen2 --version || true
# Gen2: use seed to self-host compile the El compiler
- name: Self-host compile El compiler (gen2)
# Gen3: use gen2 to compile the El compiler from its own El source (self-host)
- name: Self-host compile El compiler with gen2 (gen3)
run: |
mkdir -p dist/platform
dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c
dist/elc-gen2 el-compiler/src/compiler.el > dist/elc-gen3.c
gcc -O2 \
-I runtime \
dist/elc-gen2.c \
runtime/el_runtime.c \
-lcurl -lssl -lcrypto -lpthread -lm \
-I el-compiler/runtime \
dist/elc-gen3.c \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-o dist/platform/elc
chmod +x dist/platform/elc
echo "gen2 (self-hosted) elc built"
echo "gen3 (self-hosted) elc built"
dist/platform/elc --version || true
# Build elb binary
@@ -62,33 +71,37 @@ jobs:
mkdir -p dist/bin
dist/platform/elc elb.el > dist/elb.c
gcc -O2 \
-I runtime \
-I el-compiler/runtime \
dist/elb.c \
runtime/el_runtime.c \
-lcurl -lssl -lcrypto -lpthread -lm \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-o dist/bin/elb
chmod +x dist/bin/elb
echo "elb built"
# Build epm binary using elb (epm lives at repo root, not inside lang/)
# Build epm binary (epm lives at repo root, not inside lang/)
- name: Build epm
run: |
ABS_ELB="$(pwd)/dist/bin/elb"
ABS_ELC="$(pwd)/dist/platform/elc"
ABS_RUNTIME="$(pwd)/runtime"
ABS_OUT="$(pwd)/dist/bin"
(cd ../epm && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
dist/platform/elc ../epm/src/epm.el > dist/epm.c
gcc -O2 \
-I el-compiler/runtime \
dist/epm.c \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-o dist/bin/epm
chmod +x dist/bin/epm
echo "epm built"
# Build el-install binary using elb
# Build el-install binary
- name: Build el-install
run: |
ABS_ELB="$(pwd)/dist/bin/elb"
ABS_ELC="$(pwd)/dist/platform/elc"
ABS_RUNTIME="$(pwd)/runtime"
ABS_OUT="$(pwd)/dist/bin"
(cd tools/install && "$ABS_ELB" --clean --elc="$ABS_ELC" --runtime="$ABS_RUNTIME" --out="$ABS_OUT")
dist/platform/elc tools/install/el-install.el > dist/el-install.c
gcc -O2 \
-I el-compiler/runtime \
dist/el-install.c \
el-compiler/runtime/el_runtime.c \
-lcurl -lpthread -lm \
-o dist/bin/el-install
chmod +x dist/bin/el-install
echo "el-install built"
@@ -121,90 +134,90 @@ jobs:
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_core
-lcurl -lpthread -lm -o /tmp/el_native_core
/tmp/el_native_core
- name: Run tests - native (text)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_text
-lcurl -lpthread -lm -o /tmp/el_native_text
/tmp/el_native_text
- name: Run tests - native (string)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_string
-lcurl -lpthread -lm -o /tmp/el_native_string
/tmp/el_native_string
- name: Run tests - native (math)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_math
-lcurl -lpthread -lm -o /tmp/el_native_math
/tmp/el_native_math
- name: Run tests - native (state)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_state
-lcurl -lpthread -lm -o /tmp/el_native_state
/tmp/el_native_state
- name: Run tests - native (time)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_time
-lcurl -lpthread -lm -o /tmp/el_native_time
/tmp/el_native_time
- name: Run tests - native (json)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_json
-lcurl -lpthread -lm -o /tmp/el_native_json
/tmp/el_native_json
- name: Run tests - native (env)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_env
-lcurl -lpthread -lm -o /tmp/el_native_env
/tmp/el_native_env
- name: Run tests - native (fs)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/runtime"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
-lcurl -lssl -lcrypto -lpthread -lm -o /tmp/el_native_fs
-lcurl -lpthread -lm -o /tmp/el_native_fs
/tmp/el_native_fs
# Bundle the SDK tarball - runs from the repo root to reference lang/ paths correctly
@@ -216,10 +229,8 @@ jobs:
cp lang/dist/platform/elc dist/sdk/bin/elc
cp lang/dist/bin/elb dist/sdk/bin/elb
cp lang/dist/bin/epm dist/sdk/bin/epm
cp lang/runtime/el_runtime.c dist/sdk/runtime/
cp lang/runtime/el_runtime.h dist/sdk/runtime/
cp lang/runtime/engram_store.c dist/sdk/runtime/
cp lang/runtime/engram_store.h dist/sdk/runtime/
cp lang/el-compiler/runtime/el_runtime.c dist/sdk/runtime/
cp lang/el-compiler/runtime/el_runtime.h dist/sdk/runtime/
cp lang/runtime/*.el dist/sdk/runtime/
tar -czf dist/el-sdk-latest.tar.gz -C dist/sdk .
echo "SDK tarball bundled: dist/el-sdk-latest.tar.gz"
@@ -230,7 +241,7 @@ jobs:
if: github.event_name == 'push'
working-directory: ${{ github.workspace }}
env:
GITEA_TOKEN: ${{ secrets.GIT_TOKEN }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_API: https://git.neuralplatform.ai/api/v1
REPO: neuron-technologies/el
run: |
@@ -276,10 +287,8 @@ jobs:
# Per-file assets (downstream CI needs these individually)
upload_asset lang/dist/platform/elc elc
upload_asset lang/runtime/el_runtime.c el_runtime.c
upload_asset lang/runtime/el_runtime.h el_runtime.h
upload_asset lang/runtime/engram_store.c engram_store.c
upload_asset lang/runtime/engram_store.h engram_store.h
upload_asset lang/el-compiler/runtime/el_runtime.c el_runtime.c
upload_asset lang/el-compiler/runtime/el_runtime.h el_runtime.h
# SDK bundle and installer binary
upload_asset dist/el-sdk-latest.tar.gz el-sdk-latest.tar.gz
@@ -292,29 +301,21 @@ jobs:
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
run: |
# Fail loudly: previously this step had no `set -e`, so an auth or
# upload failure was swallowed (step exited 0 on the trailing echo)
# and the SDK silently never published. Surface failures now.
set -euo pipefail
if [ -z "${GCP_SA_KEY:-}" ]; then
echo "FATAL: GCP_SA_KEY secret is empty — cannot authenticate to publish" >&2
exit 1
fi
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
apt-get install -y -qq apt-transport-https ca-certificates curl
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
apt-get install -y -qq apt-transport-https ca-certificates gnupg curl
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" > /etc/apt/sources.list.d/google-cloud-sdk.list
apt-get update -qq && apt-get install -y google-cloud-cli
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project neuron-785695
echo "Publishing as active account: $(gcloud config get-value account 2>/dev/null)"
VERSION="${GITHUB_SHA:0:8}"
VERSION="${GITEA_SHA:0:8}"
gcloud artifacts generic upload \
--repository=foundation-prod \
--location=us-central1 \
--project=neuron-785695 \
--package=el-elc \
--package=el/elc \
--version="${VERSION}" \
--source=dist/platform/elc
@@ -322,97 +323,28 @@ jobs:
--repository=foundation-prod \
--location=us-central1 \
--project=neuron-785695 \
--package=el-elb \
--package=el/el_runtime.c \
--version="${VERSION}" \
--source=dist/bin/elb
--source=el-compiler/runtime/el_runtime.c
gcloud artifacts generic upload \
--repository=foundation-prod \
--location=us-central1 \
--project=neuron-785695 \
--package=el-runtime-c \
--package=el/el_runtime.h \
--version="${VERSION}" \
--source=runtime/el_runtime.c
gcloud artifacts generic upload \
--repository=foundation-prod \
--location=us-central1 \
--project=neuron-785695 \
--package=el-runtime-h \
--version="${VERSION}" \
--source=runtime/el_runtime.h
gcloud artifacts generic upload \
--repository=foundation-prod \
--location=us-central1 \
--project=neuron-785695 \
--package=el-runtime-js \
--version="${VERSION}" \
--source=runtime/el_runtime.js
--source=el-compiler/runtime/el_runtime.h
echo "Published El SDK version=${VERSION} to foundation-prod"
# Keep key alive for the ci-base rebuild step below
# (deleted in that step after docker push)
- name: Rebuild ci-base with fresh El SDK
# Patches ci-base:latest in-place: pulls the existing image (which has all
# system deps — Node, Go, gcloud, Docker CLI, etc.) and overlays the freshly
# built El SDK on top. Keeps the full ci-base rebuild fast and incremental.
#
# continue-on-error: this is a CI-cache optimization, NOT the release
# artifact. It runs Docker (pull/build/push ~600MB) on the host-mode GCE
# runner where DinD/Docker availability is fragile. A failure here must
# never block or redden the job — the SDK publish above is the deliverable.
continue-on-error: true
if: github.event_name == 'push'
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
run: |
set -euo pipefail
CI_BASE="us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base"
SHA="${GITHUB_SHA:0:8}"
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project neuron-785695
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
# Pull existing ci-base (system deps stay cached in the base layer)
docker pull "${CI_BASE}:latest"
# Inline Dockerfile — only replaces the El SDK layer
cat > /tmp/Dockerfile.ci-base-patch << 'EOF'
ARG BASE
FROM ${BASE}
COPY dist/platform/elc /opt/el/dist/platform/elc
COPY dist/bin/elb /opt/el/dist/bin/elb
COPY runtime/el_runtime.c /opt/el/runtime/el_runtime.c
COPY runtime/el_runtime.h /opt/el/runtime/el_runtime.h
COPY runtime/el_runtime.js /opt/el/runtime/el_runtime.js
RUN chmod +x /opt/el/dist/platform/elc /opt/el/dist/bin/elb
EOF
docker build \
--build-arg BASE="${CI_BASE}:latest" \
--build-arg BUILDKIT_INLINE_CACHE=1 \
-f /tmp/Dockerfile.ci-base-patch \
-t "${CI_BASE}:latest" \
-t "${CI_BASE}:${SHA}" \
.
docker push "${CI_BASE}:latest"
docker push "${CI_BASE}:${SHA}"
echo "ci-base rebuilt: ${CI_BASE}:latest (${SHA})"
rm -f /tmp/gcp-key.json
- name: Dispatch el-sdk-updated to downstream repos
if: github.event_name == 'push'
env:
GITEA_TOKEN: ${{ secrets.GIT_TOKEN }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_API: https://git.neuralplatform.ai/api/v1
run: |
for repo in neuron-technologies/forge neuron-technologies/neuron-web; do
for repo in neuron-technologies/forge; do
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
+2 -2
View File
@@ -6,13 +6,13 @@ set -euo pipefail
ROOT="$(git rev-parse --show-toplevel)"
LANG_DIR="$ROOT/lang"
RUNTIME="$LANG_DIR/runtime"
RUNTIME="$LANG_DIR/el-compiler/runtime"
ELC="$LANG_DIR/dist/platform/elc"
# If elc isn't built yet, skip with a warning rather than blocking
if [ ! -x "$ELC" ]; then
echo "⚠ elc not found at lang/dist/platform/elc — skipping pre-commit tests"
echo " Build it first: cd lang && gcc -O2 -I runtime dist/elc-bootstrap.c runtime/el_runtime.c -lcurl -lpthread -o dist/elc-gen2 && ./dist/elc-gen2 el-compiler/src/compiler.el > /tmp/elc.c && gcc -O2 -I runtime /tmp/elc.c runtime/el_runtime.c -lcurl -lpthread -o dist/platform/elc"
echo " Build it first: cd lang && gcc -O2 -I el-compiler/runtime dist/elc-bootstrap.c el-compiler/runtime/el_runtime.c -lcurl -lpthread -o dist/elc-gen2 && ./dist/elc-gen2 el-compiler/src/compiler.el > /tmp/elc.c && gcc -O2 -I el-compiler/runtime /tmp/elc.c el-compiler/runtime/el_runtime.c -lcurl -lpthread -o dist/platform/elc"
exit 0
fi
-154
View File
@@ -1,154 +0,0 @@
# El
**A self-hosting, statically-typed language that compiles to C — built around a graph-native runtime instead of a database driver.**
El is the execution substrate for the Neuron agent runtime, the DHARMA network, and the Engram knowledge graph. This repository is the monorepo for the whole stack: the language itself, the graph memory engine it's built to talk to natively, and the tools (package manager, IDE, UI framework, diagramming) built on top of it.
---
## Why El exists
Every other language treats persistent, associative state as something you reach for through a driver — a SQL client, an ORM, a Redis library bolted on from outside. El inverts that: graph operations (`engram_*`) are runtime primitives, on the same footing as string or list operations. There is no separate database driver because the database is not separate.
El has four defining properties:
1. **Self-hosting compiler.** The compiler (`lexer.el`, `parser.el`, `codegen.el`, `compiler.el`) is written in El. It compiles El source to C, which `cc` compiles against a fixed runtime into a native binary. A Rust genesis compiler bootstrapped the first iteration; the self-hosted binary at `lang/dist/platform/elc` has been the canonical compiler ever since — every binary in `dist/platform/` was produced by an earlier version of itself compiling `el-compiler/src/`. The chain is auditable: source is the ground truth, not the binary. See [lang/BOOTSTRAP.md](lang/BOOTSTRAP.md) for the full recovery path if that binary is ever lost.
2. **C compilation target.** Every compiled program is plain C11. Every El value is `el_val_t` (`int64_t`); strings are heap pointers cast through it. Functions become C functions; top-level statements become `main()`.
3. **Graph-native runtime.** The runtime provides first-class graph operations over an in-process Engram store — no separate DB driver, no ORM.
4. **DHARMA-aware identity.** A `cgi` block declares a program's DHARMA identity at compile time. The runtime resolves identity before user code runs, so `dharma_*` calls have a stable principal and channel surface throughout.
---
## Architecture map
```
┌─────────────┐
│ lang │ El compiler + C runtime
│ (El itself) │ everything below is written in it,
└──────┬──────┘ or compiles down through it
┌─────────────┼─────────────┐
│ │ │
┌──────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ engram │ │ epm │ │ ide │
│ graph/mem │ │ package │ │ editor + │
│ substrate │ │ manager │ │ LSP │
└──────┬─────┘ └───────────┘ └───────────┘
┌───────┼────────────────┬─────────────────────┐
│ │ │ │
┌─────▼───┐ ┌─▼──────────┐ ┌──▼──────────┐ ┌─────▼──────┐
│ elp │ │ ql │ │ ui │ │ arbor │
│ NLG / │ │engram-el. │ |spreading- │ |arbor │
│ 31 langs│ │studio+tests│ |activation UI│ |diagram lang│
└─────────┘ └────────────┘ └─────────────┘ └────────────┘
```
`lang` is the foundation — the compiler and C runtime everything else builds on. `engram` is the graph-native memory/state engine that gives El its identity (property 3 above). Everything else is either a tool for working with El (`epm`, `ide`) or a system built on top of Engram's graph model (`elp`, `ql`, `ui`, `arbor`).
---
## Repository layout
### [lang/](lang/) — the El language
The compiler and runtime. Self-hosting: `elc-cli.el``compiler.el``lexer.el` / `parser.el` / `codegen.el` / `codegen-js.el`, textually inlined and compiled in one pass. Compiles to C11 and links against `el-compiler/runtime/el_seed.c`, a hand-maintained OS-boundary layer (libcurl HTTP, pthreads, filesystem, arena allocation) — everything else in the runtime is native El (`runtime/*.el`).
Two layers to know: **El programs** (`.el` files — where nearly all work belongs) and **the C seed** (`el_seed.c` — edit only for genuine OS-level access; never re-implement what El can already express).
Current status (single source of truth: [lang/spec/language.md](lang/spec/language.md)): lexer/parser/codegen and the C runtime's core (I/O, strings, math, lists, maps, filesystem, args) are implemented. In flight: `%` operator, match-statement codegen, `?` nil-propagation, `cgi` block parsing + DHARMA identity resolution, VBD role enforcement (`@manager`/`@engine`/`@accessor`), the real `engram_*` and `dharma_*` runtimes (currently stubs), and libcurl-backed `http_get`/`http_post`/`http_serve`. Bitwise operators, `??`, and `as` casts are explicitly **not** in this language.
Key docs: [AGENTS.md](lang/AGENTS.md) (agent-facing orientation), [BOOTSTRAP.md](lang/BOOTSTRAP.md) (compiler recovery from scratch), [spec/language.md](lang/spec/language.md), [spec/codegen-js.md](lang/spec/codegen-js.md).
### [engram/](engram/) — graph intelligence substrate
**A local-first memory substrate for accumulating intelligence**, and the reason El's runtime doesn't need a database driver. Rust core (`engram-core`, `engram-ffi`) exposed to El and other languages (Kotlin, TypeScript/WASM, Go bindings).
The model: retrieval is **spreading activation**, not query. You name seed nodes and a query embedding; activation propagates outward through weighted edges, attenuating multiplicatively per hop (`strength = parent_strength × edge_weight × target_salience × cosine_sim`), gets pruned below a threshold, and the top-N nodes by activation strength come back. Storage and retrieval are the same structure — the way long-term potentiation works in biological memory, not the way a relational or vector database works.
Nodes live in four tiers (Working / Episodic / Semantic / Procedural, mirroring prefrontal / hippocampal / neocortical / cerebellar memory) and migrate between them based on **salience decay**`importance × recency-decay × log(activation_count)`. Forgetting is adaptive pruning, not a bug: unreinforced memories stop competing for attention without being deleted.
Backed by `sled` (embedded, local-first, no daemon) with flat cosine scan for vector search — deliberately simple until scale demands an HNSW layer. Full API and design rationale in [engram/README.md](engram/README.md).
### [elp/](elp/) — Engram Language Protocol
Bidirectional engine mapping between Engram semantic forms and natural-language surface text, across **31 languages** — from Spanish and Japanese through historical/liturgical languages (Old Norse, Sanskrit, Sumerian, Coptic, Akkadian, Ge'ez). Compilation order runs `language-profile` + `vocabulary` → per-language `morphology-*``grammar``realizer``semantics``elp`. This is what lets an Engram graph node round-trip to and from readable text in any of those languages.
### [epm/](epm/) — El Package Manager
Manages **vessels** (El's package unit): publish, install, resolve dependencies. Vessels are stored in Engram as graph nodes, not files in a registry index — `epm` reads the local `manifest.el`, talks to Engram over HTTP, and writes resolved vessels to `.epm/vessels/`. Source: `registry.el`, `install.el`, `update.el`, `manifest.el`.
### [ide/](ide/) — El IDE
Three vessels: **el-ide-server** (HTTP backend — file ops, build/run, LSP bridge, plugin host, settings), **el-lsp** (the language server — completion, hover, diagnostics, outline, format, type graph), and **el-plugin-host** (first-party plugin lifecycle: install/remove/enable/disable). `ide/projects/` and `ide/examples/` hold sample projects, including the canonical `hello-friends` first-program walkthrough.
### [ql/](ql/) — engram-el
The El-native integration layer for a *live* Engram server — not a library (no importable modules, no build artifact), a set of standalone `.el` programs run directly via `el run-file`. Three components: **Studio** (`studio/studio.el`, a full terminal graph explorer), a **Hebbian field-model** proof of concept, and El builtin / LLM-builtin smoke test suites. This is the reference for correct patterns when an El program uses Engram as its substrate. Spec: [ql/spec/elql.md](ql/spec/elql.md).
### [ui/](ui/) — el-ui
A frontend framework where **component state is an Engram graph and reactivity is spreading activation** — not virtual-DOM diffing (React), Proxy-based dependency tracking (Vue), or compile-time analysis (Svelte). Re-renders are activated and propagated the same way associative memory retrieval works in `engram/`.
~15 vessels covering the full frontend surface: `el-platform` (env/fs/network/clock abstraction), `el-config`, `el-html` (SSR emit primitives), `el-layout`, `el-style` (design tokens/themes), `el-i18n`, `el-auth` / `el-identity` (JWT, sessions, OAuth PKCE — Engram-native), `el-services` (REST/gRPC/WebSocket bindings), `el-aop` (`@authenticate`/`@authorize`/`@cache`/`@rate_limit` decorators), `el-secrets`, `el-graph` (graph rendering/editor), `el-publish` (App Store / Play Store automation), and `el-ui-compiler` (El→JS component compiler; currently a stub pending a JS backend in `elc`). Spec: [ui/spec/framework.md](ui/spec/framework.md).
### [arbor/](arbor/) — diagram language
A `.arbor` diagram language and toolchain: `arbor-core` (NodeId/shape/edge-kind types), `arbor-parse` (recursive-descent parser), `arbor-diagram` (IR + Mermaid serializer + architecture-diagram builders), `arbor-layout` (hierarchical layout — rank assignment, positioning, group bounds), `arbor-render` (SVG renderer), `arbor-cli`. (The architecture map above is the kind of diagram this is for.)
---
## Getting started
Install the El SDK from the latest release:
```bash
bash lang/install.sh
# EL_VERSION=v1.0.0 bash lang/install.sh # pin a specific release tag
# EL_PREFIX=/opt/el bash lang/install.sh # custom install prefix
```
Or build the compiler from source and verify the self-hosting chain:
```bash
cd lang
./dist/platform/elc elc-cli.el > elc-new.c
cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
-o dist/platform/elc-new \
elc-new.c el-compiler/runtime/el_seed.c
# Confirm the new binary reproduces itself exactly
./dist/platform/elc-new elc-cli.el > elc-verify.c
diff elc-new.c elc-verify.c # should be identical
mv dist/platform/elc-new dist/platform/elc
```
Run your first program:
```bash
./lang/dist/platform/elc lang/examples/hello.el > hello.c
cc -std=c11 -I lang/el-compiler/runtime -lcurl -lpthread \
-o hello hello.c lang/el-compiler/runtime/el_seed.c
./hello
```
More examples in [lang/examples/](lang/examples/), including a full starter project at `lang/examples/hello-project/`.
If the compiler binary is ever lost or corrupted, [lang/BOOTSTRAP.md](lang/BOOTSTRAP.md) is the authoritative recovery path.
---
## Development workflow
Branching follows `dev → stage → main`: work lands on `dev`, promotes to `stage` for integration testing, and is promoted to `main` for release (visible directly in the git history of this repo). CI is defined per-subproject under `.gitea/workflows/``lang`/`epm`/`ide` share the root pipeline; `engram` and `ql` carry their own (`ci-dev`, `ci-stage`, and a release workflow each).
- Language/runtime specs live at `*/spec/*.md` (`lang/spec/`, `ql/spec/`, `ui/spec/`) and are the single source of truth for implemented-vs-planned status — code and docs are expected to agree with the spec's status markers, not the other way around.
- Agent-facing orientation guides live at `*/AGENTS.md` (currently `lang/AGENTS.md`); more subprojects may grow their own as they need agent-specific conventions documented.
- Tagged releases live under `lang/releases/`, each with its own `RELEASE.md`.
---
## Status
This is an actively developed, internal monorepo — not yet published under an open license. Treat everything here as proprietary to Neuron Technologies unless told otherwise.
@@ -1,107 +0,0 @@
# Reasoning Operators — Decisions & Reversal
**Date:** 2026-08-13
**Branch:** `engram-tiered-storage` (worktree `/tmp/engram-tiered-wt`)
**Status:** staged locally — NOT pushed, NOT tagged, NOT merged. Live `:8742` untouched.
## What this adds
A **REASONING layer** built as pure C compositions over the already-live §5 geometry
OPERATORS (`engram_geometry.{h,c}`: overlap, subtract, setdiff, combine, distance,
analogy). Where the operators are a relational algebra over neighborhood descriptors,
these are reasoning *modes* built by chaining that algebra. New files:
- `lang/runtime/engram_reason.h` — public API for the five modes + a shared
point-to-manifold fit primitive.
- `lang/runtime/engram_reason.c` — implementations. READ-ONLY over descriptor inputs,
`stdlib + libm` only, touches no store / index / activation. All geometry is
delegated to `engram_geo_*`; this file only composes.
- `engram/test/test_reason.c` + `engram/test/run_reason_tests.sh` — closed-form
constructed tests (hand-built descriptors with known answers), PERF + ASan/UBSan.
El-exposure (pass-through, no self-host fold):
- `lang/runtime/el_runtime.c``+#include "engram_reason.h"` and the builtin
`engram_reason_analogy_json(a_csv,b_csv,c_csv)`.
- `lang/runtime/el_runtime.h` — its declaration.
- `lang/runtime/el_seed.c` — native `__engram_reason_analogy_json` wrapper (same
C-table wiring as the §5 ops).
## The five modes — signatures & composition
| Mode | C entry point | Composes |
|------|---------------|----------|
| **ANALOGY** `A:B :: C:?` | `engram_reason_analogy(A,B,C,candidates,n,out)` | `engram_geo_analogy` (Procrustes R) + `engram_geo_analogy_apply` + centroid L2. Learns `R_{A→B}` = `engram_geo_analogy(B,A)` (that op returns R with `apply(R, Y-axis)≈X-axis`), reconstructs the residual translation `t = c_B R·c_A`, maps `mapped = R·c_C + t`, ranks candidates by distance. |
| **INDUCTION** `{E_i}→rule` | `engram_reason_induce(examples,n,top_axes,ext_floor,out)` + `engram_reason_membership` | `engram_geo_combine` folded left→right → pooled "rule" descriptor; shared subspace surfaces as the dominant pooled axes. Membership = point-to-manifold fit. |
| **ABDUCTION** `x→best H` | `engram_reason_abduce(obs,dim,hyps,n,ext_floor,out)` | shared `engram_reason_point_fit` against each hypothesis; argmax fit score; full ranking. |
| **CAUSAL** `x?y \| Z,t` | `engram_reason_causal(x,y,confounders,nZ,t_x,t_y,drop_frac,out)` | centroid cosine (raw correlation) + `engram_geo_subtract` residual-centroid (control for each confounder, take the strongest single explainer) + temporal precedence. Verdict `DIRECTED` / `CONFOUNDED` / `NONE` + a `confounded` flag. |
| **PLANNING** `start→goal` | `engram_reason_plan(nodes,n,start,goal,radius,use_w,out)` | `engram_geo_distance` as edge weights over neighborhoods within `radius`; O(n²) Dijkstra → discrete geodesic path. |
Shared primitive `engram_reason_point_fit` splits `(x centroid)` into an in-subspace
Mahalanobis distance (scaled by axis extents) and an orthogonal off-model residual;
it is the single engine under INDUCTION's membership test and ABDUCTION's ranking.
## Proof (DONE-WITH-PROOF)
`engram/test/run_reason_tests.sh`: **33/33 checks, 0 failures** on BOTH passes
(PERF -O2, and ASan+UBSan). macOS `leaks --atExit`: **0 leaks / 0 total leaked bytes**.
Per-mode closed-form assertions actually exercised:
- **ANALOGY** — A→B = +90° rotation in e0-e1 plane + a +5 shift in e2; Procrustes
residual `~0`; predicted point `(0,2,5,0)` recovered exactly; nearest candidate =
the planted true D (index 1), distance `~0`.
- **INDUCTION** — 3 examples sharing span(e0,e1) (extents 1.0 / 0.8) each with a small
idiosyncratic axis (e2 or e3); induced top-2 axes lie in span(e0,e1) (extents
recovered ~1.0 / ~0.8); held-out in-plane point fits (membership 0.885), off-subspace
point rejected (0.100), in-plane-but-far point rejected (0.039).
- **ABDUCTION** — observation planted inside H1 among {H0,H1,H2}; best = H1, rank[0] = H1,
H1 smallest distance.
- **CAUSAL** — chain A→B→C along e0 (t 1<2<3) + confounder Z(e1) that leaks into A and
drives D(t=4): A→B and B→C flagged `DIRECTED` with correct precedence and association
that survives control; AD `CONFOUNDED` (raw |cos|=0.707 collapses to 0.0 under
control) with `confounded=1`; BD `NONE` (no association).
- **PLANNING** — 6 neighborhoods on a semicircle (r=10); `neighbor_radius=7` admits only
consecutive hops; plan = `[0,1,2,3,4,5]` (the arc), cost `30.90` (> the 20-unit chord,
confirming it is the geodesic through the manifold, not a straight jump); a too-small
radius correctly yields `reached=0`.
## El-exposure status
- **ANALOGY is el-callable** via the same pass-through the §5 operators use. Proof: a
container-capped fold (`capfold.sh`, peak ~0 GB) of a demo `.el` through the shipped
`lang/dist/platform/elc` emits a *direct C call* `engram_reason_analogy_json(A,B,A)`
(no registration, no self-host fold); the generated C links against `el_runtime.c` +
`engram_reason.c` + geometry/store/vindex and runs end-to-end. (The standalone demo's
store copy boots 0 nodes — a pre-existing quirk that hits the *shipped geo demo
identically* — so the call returns `{"error":"geometry unavailable"}`; this still proves
the compiled El → C reasoning symbol → JSON chain executes. Numeric correctness on real
data is covered by the C test.) This compile also confirms `el_runtime.c` +
`engram_reason.c` compile and link clean.
- **INDUCTION / ABDUCTION / CAUSAL / PLANNING are C-layer only for now.** Their inputs are
candidate *sets*, raw *points*, and *timestamps* that do not map to the flat comma-
separated-seed El ABI. A richer marshalling surface would touch the codegen/registration
path and risk an uncapped fold — explicitly deferred per the hard rail. The C functions
are fully proven and callable from any C caller today.
## Build wiring (for the later cutover/durability pass)
`engram_reason.c` must be added to the engram server link line **alongside**
`engram_geometry.c` (the heavy-runtime path `cc dist/engram.c el_runtime.c
engram_store.c engram_geometry.c engram_vindex.c …`). `el_runtime.c` now
`#include`s `engram_reason.h` and references `engram_reason_analogy_json`, so a build
that omits `engram_reason.c` will fail to link that symbol. One-line addition, same as
how `engram_geometry.c` was originally added.
## Reversal
Fully additive; nothing existing was modified in behavior. To revert:
1. Delete `lang/runtime/engram_reason.h`, `lang/runtime/engram_reason.c`,
`engram/test/test_reason.c`, `engram/test/run_reason_tests.sh`, and this doc.
2. In `lang/runtime/el_runtime.c`: remove `#include "engram_reason.h"` and the
`engram_reason_analogy_json` function.
3. In `lang/runtime/el_runtime.h`: remove the `engram_reason_analogy_json` declaration.
4. In `lang/runtime/el_seed.c`: remove the `__engram_reason_analogy_json` wrapper.
5. Remove `engram_reason.c` from any server link line if the cutover added it.
No store, schema, config, WAL, or on-disk format was touched; no data migration exists,
so reversal is a pure code removal with no state to undo.
@@ -1,128 +0,0 @@
# Verifier Layer — Grounding + Consistency (decisions + reversal)
**Date:** 2026-08-13
**Branch:** `engram-tiered-storage` (worktree `/tmp/engram-tiered-wt`), atop `a3358df`
**Scope:** additive, read-only, staged. No push, no tag, no merge. Live `:8742` untouched.
## What this adds
The VERIFIER layer — the "disposes" half of the propose→verify loop. The geometry
PROPOSES (cheap, creative, sometimes wrong); the verifier DISPOSES, catching the
class of failure no grammar check sees: a fluent, confident, WRONG output — the
**plausible lie**.
Motivating failure (tonight's PT translation): a deleted negation turned
"you never fought" into "you argued" — reassurance inverted into accusation,
grammatical and invisible, catchable only by the geometry.
Two checks, both pure C11 (stdlib + libm), read-only over their inputs, touching no
store / index / activation. Every geometry op is delegated to the already-shipped
reasoning + §5 operator primitives; this layer only composes and thresholds.
### Files added
- `lang/runtime/engram_verify.h` — API + design contract.
- `lang/runtime/engram_verify.c` — implementation.
- `engram/test/test_verify.c` — closed-form constructed cases (29 checks).
- `engram/test/run_verify_tests.sh` — two-pass runner (PERF, then ASan/UBSan).
No existing file was modified.
## C signatures + how each composes the existing primitives
### GROUNDING (anti-hallucination)
```c
int engram_verify_grounding(const float* claim, int dim,
const GeoDescriptor* const* evidence, int n_evidence,
double ext_floor, double ground_threshold,
GeoGrounding* out);
```
Fits the claim POINT against every real evidence neighborhood via
`engram_reason_point_fit` (in-distribution Mahalanobis + off-model orthogonal
residual) and keeps the BEST supporter. Grounded iff best fit score ≥
`ground_threshold`. Deliberately an ABSOLUTE-THRESHOLD gate, distinct from ABDUCTION
(which always ranks and picks a winner): grounding asks the prior question — "is there
any real support at all?" — and may answer no. The off-model `best_ortho` residual is
the sharpest hallucination signal: energy in a direction the manifold does not span.
### CONSISTENCY (contradiction detection)
```c
int engram_verify_consistency(const float* claim, int dim,
const GeoDescriptor* context,
const GeoDescriptor* pole_pos, const GeoDescriptor* pole_neg,
const GeoDescriptor* forbidden,
double ext_floor, double deadzone_frac,
double forbidden_thresh, double max_distance,
GeoConsistency* out);
```
Two independent sub-checks (either can fire; both flags reported):
- **(a) POLARITY / negation inversion** — the reassurance→accusation catch.
A polarity axis `p = (c_pos c_neg)/‖·‖` is defined by two REAL poles (affirm vs
negate), midpoint `o = ½(c_pos + c_neg)`. Signed sides: `claim_side = p·(claim o)`,
`ref_side = p·(c_context o)`. If they have OPPOSITE sign and both clear the neutral
deadzone (`deadzone_frac·½‖c_posc_neg‖`), the claim asserts the polarity opposite to
the grounded truth → inversion flagged. Pure dot products / projections over the same
centroids the geometry already computes.
- **(b) GEOMETRIC contradiction** — claim sits INSIDE a `forbidden` region it must be
far from (`engram_reason_point_fit` score ≥ `forbidden_thresh`), OR violates a
max-distance constraint to `context` (`L2 > max_distance`).
## Proof (constructed cases — demonstrate, not declare)
`./engram/test/run_verify_tests.sh`**29 checks, 0 failures** in BOTH passes
(PERF -O2, and ASan+UBSan -O1). macOS `leaks --atExit`: **0 leaks for 0 total leaked
bytes**.
Key demonstrated numbers:
- Grounding IN (claim inside E0): score 0.885, grounded=1, ortho≈0.
- Grounding OUT (claim floating along unmodeled e2): score 0.0004, grounded=0,
ortho=50.0 (the hallucination signal), nearest centroid L2=50.
- **Negation inversion (the catch):** truth "never fought" ref_side=5.0, lie
"you argued" claim_side=+4.0 → opposite poles → `inverted=1`, verdict=POLARITY,
consistency=0. Faithful claim (4.0, same pole) → inverted=0, verdict=OK,
consistency=1. Neutral claim inside deadzone → not triggered.
- Geometric: claim inside forbidden region → geo_violation=1 (forb_fit 0.99);
claim beyond max_distance → geo_violation=1 (ctx_dist 8.0 > 3.0).
- **Combined (the whole point):** a claim that is GROUNDED in real vocabulary
(grounded=1, score 1.0) yet polarity-inverted is PASSED by grounding and CAUGHT
only by consistency (verdict=POLARITY). Grounding alone is insufficient; consistency
is the catch.
## el-exposure — DEFERRED (matches reasoning-agent precedent)
Not exposed as el builtins this pass. The reasoning agent exposed ONLY `analogy`
(three seed-identified neighborhoods → the clean fixed-arity seed-CSV→descriptor JSON
pattern) and deferred its point-input / variadic-set modes (abduction, induction,
causal, planning). The verifier's grounding (claim POINT + variadic evidence SET) and
consistency (claim POINT + context + two poles + forbidden + scalar thresholds) are
exactly those shapes: no clean fixed-arity seed-CSV JSON mapping exists, and adding one
would require new JSON list-of-lists + point-vector marshaling absent from the codebase,
risking an elc rebuild/fold (violates the capped-fold-only rail). A claim is also an
arbitrary proposed POINT, not necessarily an existing node — so the point-native C API
is the correct primitive. Deferred deliberately; the C layer is complete and proven.
When exposed later, follow the same additive pass-through pattern used for the geo
operators: native `engram_verify_*_json(el_val_t ...)` in `el_runtime.c` (heavy runtime)
+ a `__engram_verify_*_json` wrapper in `el_seed.c`, resolving seed CSVs → descriptors
and marshaling the claim vector — no fold needed for callability (shipped elc passes
unknown-ident builtin calls straight through to the heavy-runtime C symbols).
## Reach checks (FORMAL / CAUSAL / PREDICTIVE) — NOT STARTED
Honestly not started this pass; the two tractable-now checks (grounding + consistency)
were driven to done-with-proof first as specified.
- **CAUSAL** already exists as a REASONING operator (`engram_reason_causal`,
intervention/temporal-precedence over the typed causal graph); a verifier wrapper that
checks "does the claimed cause actually precede/influence" would compose it — not built.
- **FORMAL** (logical consistency of a claim SET) needs an external checker (SMT/proof
kernel) — the non-geometric seam; not built.
- **PREDICTIVE** (commit a prediction, check vs outcome, restructure on error) is the CGI
research frontier; not built.
## Reversal
Fully additive. To reverse: delete the four added files
(`lang/runtime/engram_verify.{h,c}`, `engram/test/test_verify.c`,
`engram/test/run_verify_tests.sh`) or `git revert` this commit. Nothing else references
them; no build wiring, no el registration, no store schema, no runtime path was changed.
Live `:8742` was never touched.
+3 -3
View File
@@ -1,7 +1,7 @@
// auto-generated by elc --emit-header — do not edit
extern fn sem_get(json: String, key: String) -> String
extern fn generate_frame(frame: [String]) -> String
extern fn generate_frame_lang(frame: [String], lang_code: String) -> String
extern fn build_form_from_json(semantic_form_json: String, lang_code: String) -> [String]
extern fn generate_frame(frame: Any) -> String
extern fn generate_frame_lang(frame: Any, lang_code: String) -> String
extern fn build_form_from_json(semantic_form_json: String, lang_code: String) -> Any
extern fn generate(semantic_form_json: String) -> String
extern fn generate_lang(semantic_form_json: String, lang_code: String) -> String
+28 -28
View File
@@ -1,22 +1,22 @@
// auto-generated by elc --emit-header do not edit
extern fn slots_get(slots: [String], key: String) -> String
extern fn slots_set(slots: [String], key: String, val: String) -> [String]
extern fn make_slots(k0: String, v0: String) -> [String]
extern fn make_slots2(k0: String, v0: String, k1: String, v1: String) -> [String]
extern fn make_slots3(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String) -> [String]
extern fn make_slots4(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String) -> [String]
extern fn make_slots5(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String, k4: String, v4: String) -> [String]
extern fn rule_id(rule: [String]) -> String
extern fn rule_lhs(rule: [String]) -> String
extern fn rule_rhs_len(rule: [String]) -> Int
extern fn rule_rhs(rule: [String], idx: Int) -> String
extern fn make_rule(id: String, lhs: String, r0: String) -> [String]
extern fn make_rule2(id: String, lhs: String, r0: String, r1: String) -> [String]
extern fn make_rule3(id: String, lhs: String, r0: String, r1: String, r2: String) -> [String]
extern fn make_rule4(id: String, lhs: String, r0: String, r1: String, r2: String, r3: String) -> [String]
extern fn build_rules() -> [[String]]
extern fn get_rules() -> [[String]]
extern fn find_rule(rule_id_str: String) -> [String]
// auto-generated by elc --emit-header - do not edit
extern fn slots_get(slots: Any, key: String) -> String
extern fn slots_set(slots: Any, key: String, val: String) -> Any
extern fn make_slots(k0: String, v0: String) -> Any
extern fn make_slots2(k0: String, v0: String, k1: String, v1: String) -> Any
extern fn make_slots3(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String) -> Any
extern fn make_slots4(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String) -> Any
extern fn make_slots5(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String, k4: String, v4: String) -> Any
extern fn rule_id(rule: Any) -> String
extern fn rule_lhs(rule: Any) -> String
extern fn rule_rhs_len(rule: Any) -> Int
extern fn rule_rhs(rule: Any, idx: Int) -> String
extern fn make_rule(id: String, lhs: String, r0: String) -> Any
extern fn make_rule2(id: String, lhs: String, r0: String, r1: String) -> Any
extern fn make_rule3(id: String, lhs: String, r0: String, r1: String, r2: String) -> Any
extern fn make_rule4(id: String, lhs: String, r0: String, r1: String, r2: String, r3: String) -> Any
extern fn build_rules() -> Any
extern fn get_rules() -> Any
extern fn find_rule(rule_id_str: String) -> Any
extern fn make_leaf(label: String, word: String) -> String
extern fn make_node1(label: String, child0: String) -> String
extern fn make_node2(label: String, child0: String, child1: String) -> String
@@ -24,15 +24,15 @@ extern fn make_node3(label: String, child0: String, child1: String, child2: Stri
extern fn make_node4(label: String, child0: String, child1: String, child2: String, child3: String) -> String
extern fn nlg_is_ws(c: String) -> Bool
extern fn skip_ws(s: String, pos: Int) -> Int
extern fn scan_token(s: String, start: Int) -> [String]
extern fn scan_token(s: String, start: Int) -> Any
extern fn render_tree(tree: String) -> String
extern fn gram_word_order(profile: [String]) -> String
extern fn gram_order_constituents(subj: String, verb: String, obj: String, profile: [String]) -> String
extern fn gram_build_vp(verb: String, aux: String, profile: [String]) -> String
extern fn gram_question_strategy(profile: [String]) -> String
extern fn gram_word_order(profile: Any) -> String
extern fn gram_order_constituents(subj: String, verb: String, obj: String, profile: Any) -> String
extern fn gram_build_vp(verb: String, aux: String, profile: Any) -> String
extern fn gram_question_strategy(profile: Any) -> String
extern fn is_pronoun(word: String) -> Bool
extern fn build_np(referent: String, slots: [String]) -> String
extern fn build_np(referent: String, slots: Any) -> String
extern fn build_pp(loc: String) -> String
extern fn build_vp_body(slots: [String]) -> String
extern fn build_vp_from_slots(slots: [String]) -> String
extern fn generate_tree(rule_id_str: String, slots: [String]) -> String
extern fn build_vp_body(slots: Any) -> String
extern fn build_vp_from_slots(slots: Any) -> String
extern fn generate_tree(rule_id_str: String, slots: Any) -> String
+46 -46
View File
@@ -1,46 +1,46 @@
// auto-generated by elc --emit-header do not edit
extern fn lang_profile(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String]
extern fn lang_get(profile: [String], key: String) -> String
extern fn lang_profile_en() -> [String]
extern fn lang_profile_ja() -> [String]
extern fn lang_profile_ar() -> [String]
extern fn lang_profile_zh() -> [String]
extern fn lang_profile_de() -> [String]
extern fn lang_profile_es() -> [String]
extern fn lang_profile_fi() -> [String]
extern fn lang_profile_sw() -> [String]
extern fn lang_profile_hi() -> [String]
extern fn lang_profile_ru() -> [String]
extern fn lang_profile_fr() -> [String]
extern fn lang_profile_la() -> [String]
extern fn lang_profile_he() -> [String]
extern fn lang_profile_sa() -> [String]
extern fn lang_profile_got() -> [String]
extern fn lang_profile_non() -> [String]
extern fn lang_profile_enm() -> [String]
extern fn lang_profile_pi() -> [String]
extern fn lang_profile_grc() -> [String]
extern fn lang_profile_ang() -> [String]
extern fn lang_profile_fro() -> [String]
extern fn lang_profile_goh() -> [String]
extern fn lang_profile_sga() -> [String]
extern fn lang_profile_txb() -> [String]
extern fn lang_profile_peo() -> [String]
extern fn lang_profile_akk() -> [String]
extern fn lang_profile_uga() -> [String]
extern fn lang_profile_egy() -> [String]
extern fn lang_profile_sux() -> [String]
extern fn lang_profile_gez() -> [String]
extern fn lang_profile_cop() -> [String]
extern fn lang_from_code(code: String) -> [String]
extern fn lang_default() -> [String]
extern fn lang_is_isolating(profile: [String]) -> Bool
extern fn lang_is_agglutinative(profile: [String]) -> Bool
extern fn lang_is_fusional(profile: [String]) -> Bool
extern fn lang_is_polysynthetic(profile: [String]) -> Bool
extern fn lang_is_rtl(profile: [String]) -> Bool
extern fn lang_has_null_subject(profile: [String]) -> Bool
extern fn lang_has_case(profile: [String]) -> Bool
extern fn lang_has_gender(profile: [String]) -> Bool
extern fn lang_word_order(profile: [String]) -> String
extern fn lang_code(profile: [String]) -> String
// auto-generated by elc --emit-header - do not edit
extern fn lang_profile(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> Any
extern fn lang_get(profile: Any, key: String) -> String
extern fn lang_profile_en() -> Any
extern fn lang_profile_ja() -> Any
extern fn lang_profile_ar() -> Any
extern fn lang_profile_zh() -> Any
extern fn lang_profile_de() -> Any
extern fn lang_profile_es() -> Any
extern fn lang_profile_fi() -> Any
extern fn lang_profile_sw() -> Any
extern fn lang_profile_hi() -> Any
extern fn lang_profile_ru() -> Any
extern fn lang_profile_fr() -> Any
extern fn lang_profile_la() -> Any
extern fn lang_profile_he() -> Any
extern fn lang_profile_sa() -> Any
extern fn lang_profile_got() -> Any
extern fn lang_profile_non() -> Any
extern fn lang_profile_enm() -> Any
extern fn lang_profile_pi() -> Any
extern fn lang_profile_grc() -> Any
extern fn lang_profile_ang() -> Any
extern fn lang_profile_fro() -> Any
extern fn lang_profile_goh() -> Any
extern fn lang_profile_sga() -> Any
extern fn lang_profile_txb() -> Any
extern fn lang_profile_peo() -> Any
extern fn lang_profile_akk() -> Any
extern fn lang_profile_uga() -> Any
extern fn lang_profile_egy() -> Any
extern fn lang_profile_sux() -> Any
extern fn lang_profile_gez() -> Any
extern fn lang_profile_cop() -> Any
extern fn lang_from_code(code: String) -> Any
extern fn lang_default() -> Any
extern fn lang_is_isolating(profile: Any) -> Bool
extern fn lang_is_agglutinative(profile: Any) -> Bool
extern fn lang_is_fusional(profile: Any) -> Bool
extern fn lang_is_polysynthetic(profile: Any) -> Bool
extern fn lang_is_rtl(profile: Any) -> Bool
extern fn lang_has_null_subject(profile: Any) -> Bool
extern fn lang_has_case(profile: Any) -> Bool
extern fn lang_has_gender(profile: Any) -> Bool
extern fn lang_word_order(profile: Any) -> String
extern fn lang_code(profile: Any) -> String
-1
View File
@@ -56,7 +56,6 @@
// String helpers
import "morphology.el"
fn akk_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn akk_str_ends(s: String, suf: String) -> Bool
extern fn akk_str_len(s: String) -> Int
extern fn akk_str_drop_last(s: String, n: Int) -> String
-1
View File
@@ -36,7 +36,6 @@
// String helpers
import "morphology.el"
fn ang_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn ang_str_ends(s: String, suf: String) -> Bool
extern fn ang_str_drop_last(s: String, n: Int) -> String
extern fn ang_str_last_char(s: String) -> String
-1
View File
@@ -21,7 +21,6 @@
// String helpers
import "morphology.el"
fn ar_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn ar_str_ends(s: String, suf: String) -> Bool
extern fn ar_str_len(s: String) -> Int
extern fn ar_str_drop_last(s: String, n: Int) -> String
-1
View File
@@ -54,7 +54,6 @@
// String helpers
import "morphology.el"
fn cop_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn cop_str_ends(s: String, suf: String) -> Bool
extern fn cop_str_len(s: String) -> Int
extern fn cop_drop(s: String, n: Int) -> String
-1
View File
@@ -26,7 +26,6 @@
// Dat: dem der dem den
// Gen: des der des der
import "morphology.el"
fn de_article_def(gender: String, gram_case: String, number: String) -> String {
if str_eq(number, "pl") {
if str_eq(gram_case, "nom") { return "die" }
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn de_article_def(gender: String, gram_case: String, number: String) -> String
extern fn de_article_indef(gender: String, gram_case: String, number: String) -> String
extern fn de_article(gender: String, gram_case: String, number: String, definite: String) -> String
-1
View File
@@ -52,7 +52,6 @@
// String helpers
import "morphology.el"
fn egy_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn egy_str_ends(s: String, suf: String) -> Bool
extern fn egy_str_len(s: String) -> Int
extern fn egy_drop(s: String, n: Int) -> String
-1
View File
@@ -31,7 +31,6 @@
// String helpers
import "morphology.el"
fn enm_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn enm_str_ends(s: String, suf: String) -> Bool
extern fn enm_drop(s: String, n: Int) -> String
extern fn enm_first_char(s: String) -> String
-1
View File
@@ -12,7 +12,6 @@
// String helpers (local, matching morphology.el conventions)
import "morphology.el"
fn es_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn es_str_ends(s: String, suf: String) -> Bool
extern fn es_str_drop_last(s: String, n: Int) -> String
extern fn es_str_last_char(s: String) -> String
-1
View File
@@ -25,7 +25,6 @@
// If only neutral vowels are found, default to "front" (the conservative choice
// for borrowed words and those without clear back vowels).
import "morphology.el"
fn fi_harmony(word: String) -> String {
let n: Int = str_len(word)
let i: Int = n - 1
+3 -3
View File
@@ -1,11 +1,11 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn fi_harmony(word: String) -> String
extern fn fi_suffix(base: String, harmony: String) -> String
extern fn fi_noun_case(stem: String, gram_case: String, number: String, harmony: String) -> String
extern fn fi_str_last_char(s: String) -> String
extern fn fi_apply_case(noun: String, gram_case: String, number: String) -> String
extern fn fi_verb_stem(dict_form: String) -> String
extern fn fi_irregular_verb(dict_form: String) -> [String]
extern fn fi_irregular_verb(dict_form: String) -> Any
extern fn fi_present_ending(stem: String, person: String, number: String, harmony: String) -> String
extern fn fi_past_stem(stem: String) -> String
extern fn fi_past_ending(stem: String, person: String, number: String, harmony: String) -> String
@@ -14,4 +14,4 @@ extern fn fi_negative(verb: String, person: String, number: String) -> String
extern fn fi_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn fi_question_suffix(harmony: String) -> String
extern fn fi_make_question(verb_form: String, harmony: String) -> String
extern fn fi_full_paradigm(noun: String) -> [String]
extern fn fi_full_paradigm(noun: String) -> Any
-1
View File
@@ -19,7 +19,6 @@
// String helpers (local, matching morphology.el conventions)
import "morphology.el"
fn fr_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn fr_str_ends(s: String, suf: String) -> Bool
extern fn fr_str_drop_last(s: String, n: Int) -> String
extern fn fr_str_last_char(s: String) -> String
-1
View File
@@ -53,7 +53,6 @@
// String helpers
import "morphology.el"
fn fro_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn fro_str_ends(s: String, suf: String) -> Bool
extern fn fro_drop(s: String, n: Int) -> String
extern fn fro_slot(person: String, number: String) -> Int
-1
View File
@@ -64,7 +64,6 @@
// String helpers
import "morphology.el"
fn gez_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn gez_str_ends(s: String, suf: String) -> Bool
extern fn gez_str_len(s: String) -> Int
extern fn gez_str_drop_last(s: String, n: Int) -> String
-1
View File
@@ -48,7 +48,6 @@
// String helpers
import "morphology.el"
fn goh_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn goh_str_ends(s: String, suf: String) -> Bool
extern fn goh_drop(s: String, n: Int) -> String
extern fn goh_slot(person: String, number: String) -> Int
-1
View File
@@ -49,7 +49,6 @@
// String helpers
import "morphology.el"
fn got_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn got_str_ends(s: String, suf: String) -> Bool
extern fn got_str_drop_last(s: String, n: Int) -> String
extern fn got_slot(person: String, number: String) -> Int
-1
View File
@@ -31,7 +31,6 @@
// String helpers
import "morphology.el"
fn grc_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn grc_str_ends(s: String, suf: String) -> Bool
extern fn grc_str_drop_last(s: String, n: Int) -> String
extern fn grc_str_last_char(s: String) -> String
-1
View File
@@ -51,7 +51,6 @@
// String helpers
import "morphology.el"
fn he_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn he_str_ends(s: String, suf: String) -> Bool
extern fn he_str_len(s: String) -> Int
extern fn he_str_drop_last(s: String, n: Int) -> String
-1
View File
@@ -24,7 +24,6 @@
// String helpers
import "morphology.el"
fn hi_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn hi_str_ends(s: String, suf: String) -> Bool
extern fn hi_str_drop_last(s: String, n: Int) -> String
extern fn hi_str_last_char(s: String) -> String
-1
View File
@@ -23,7 +23,6 @@
// Note: this is a heuristic classifier for romanized input. For production use
// with native kana/kanji forms, the dictionary form (辞書形) must be consulted.
import "morphology.el"
fn ja_verb_group(dict_form: String) -> String {
// Irregular verbs (exact match on dictionary form)
if str_eq(dict_form, "する") { return "irregular" }
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn ja_verb_group(dict_form: String) -> String
extern fn ja_ichidan_stem(dict_form: String) -> String
extern fn ja_godan_stem_change(dict_form: String, row: String) -> String
-1
View File
@@ -25,7 +25,6 @@
// String helpers
import "morphology.el"
fn la_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn la_str_ends(s: String, suf: String) -> Bool
extern fn la_str_drop_last(s: String, n: Int) -> String
extern fn la_str_last_char(s: String) -> String
-1
View File
@@ -27,7 +27,6 @@
// String helpers
import "morphology.el"
fn non_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn non_str_ends(s: String, suf: String) -> Bool
extern fn non_drop(s: String, n: Int) -> String
extern fn non_last(s: String) -> String
-1
View File
@@ -31,7 +31,6 @@
// String helpers
import "morphology.el"
fn peo_drop(s: String, n: Int) -> String {
let len: Int = str_len(s)
if n >= len { return "" }
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn peo_drop(s: String, n: Int) -> String
extern fn peo_ends(s: String, suf: String) -> Bool
extern fn peo_slot(person: String, number: String) -> Int
-1
View File
@@ -30,7 +30,6 @@
// String helpers
import "morphology.el"
fn pi_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn pi_str_ends(s: String, suf: String) -> Bool
extern fn pi_drop(s: String, n: Int) -> String
extern fn pi_last_char(s: String) -> String
-1
View File
@@ -35,7 +35,6 @@
// The heuristic returns the most probable gender. Caller should override
// for known exceptions (путь, рубль are masc despite ).
import "morphology.el"
fn ru_gender(noun: String) -> String {
let n: Int = str_len(noun)
if n == 0 { return "m" }
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn ru_gender(noun: String) -> String
extern fn ru_stem_type(noun: String, gender: String) -> String
extern fn ru_noun_case(noun: String, gender: String, gram_case: String, number: String) -> String
-1
View File
@@ -42,7 +42,6 @@
// String helpers
import "morphology.el"
fn sa_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn sa_str_ends(s: String, suf: String) -> Bool
extern fn sa_str_drop_last(s: String, n: Int) -> String
extern fn sa_slot(person: String, number: String) -> Int
-1
View File
@@ -31,7 +31,6 @@
// String helpers
import "morphology.el"
fn sga_drop(s: String, n: Int) -> String {
let len: Int = str_len(s)
if n >= len { return "" }
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn sga_drop(s: String, n: Int) -> String
extern fn sga_first(s: String) -> String
extern fn sga_rest(s: String) -> String
-1
View File
@@ -53,7 +53,6 @@
// String helpers
import "morphology.el"
fn sux_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn sux_str_ends(s: String, suf: String) -> Bool
extern fn sux_str_drop_last(s: String, n: Int) -> String
extern fn sux_str_last_char(s: String) -> String
-1
View File
@@ -24,7 +24,6 @@
// String helpers
import "morphology.el"
fn sw_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn sw_str_ends(s: String, suf: String) -> Bool
extern fn sw_str_drop_last(s: String, n: Int) -> String
extern fn sw_str_first_char(s: String) -> String
-1
View File
@@ -30,7 +30,6 @@
// String helpers
import "morphology.el"
fn txb_drop(s: String, n: Int) -> String {
let len: Int = str_len(s)
if n >= len { return "" }
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn txb_drop(s: String, n: Int) -> String
extern fn txb_ends(s: String, suf: String) -> Bool
extern fn txb_slot(person: String, number: String) -> Int
-1
View File
@@ -48,7 +48,6 @@
// String helpers
import "morphology.el"
fn uga_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn uga_str_ends(s: String, suf: String) -> Bool
extern fn uga_str_len(s: String) -> Int
extern fn uga_str_drop_last(s: String, n: Int) -> String
-11
View File
@@ -33,17 +33,6 @@
// String helpers
import "language-profile.el"
import "morphology-es.el"
import "morphology-fr.el"
import "morphology-de.el"
import "morphology-ru.el"
import "morphology-fi.el"
import "morphology-ar.el"
import "morphology-hi.el"
import "morphology-sw.el"
import "morphology-la.el"
import "morphology-ja.el"
fn str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
+5 -5
View File
@@ -1,4 +1,4 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn str_ends(s: String, suf: String) -> Bool
extern fn str_last_char(s: String) -> String
extern fn str_last2(s: String) -> String
@@ -8,7 +8,7 @@ extern fn is_vowel(c: String) -> Bool
extern fn morph_apply_suffix(base: String, suffix: String) -> String
extern fn en_irregular_plural(word: String) -> String
extern fn en_irregular_singular(word: String) -> String
extern fn en_irregular_verb(base: String) -> [String]
extern fn en_irregular_verb(base: String) -> Any
extern fn en_verb_3sg(base: String) -> String
extern fn en_should_double_final(base: String) -> Bool
extern fn en_verb_past(base: String) -> String
@@ -16,10 +16,10 @@ extern fn en_verb_gerund(base: String) -> String
extern fn en_pluralize_regular(singular: String) -> String
extern fn en_verb_form(base: String, tense: String, person: String, number: String) -> String
extern fn agree_determiner(det: String, noun: String) -> String
extern fn morph_pluralize(noun: String, profile: [String]) -> String
extern fn morph_pluralize(noun: String, profile: Any) -> String
extern fn morph_map_canonical(verb: String, code: String) -> String
extern fn morph_conjugate(verb: String, tense: String, person: String, number: String, profile: [String]) -> String
extern fn morph_inflect(word: String, features: String, profile: [String]) -> String
extern fn morph_conjugate(verb: String, tense: String, person: String, number: String, profile: Any) -> String
extern fn morph_inflect(word: String, features: String, profile: Any) -> String
extern fn pluralize(singular: String) -> String
extern fn singularize(plural: String) -> String
extern fn verb_form(base: String, tense: String, person: String, number: String) -> String
+5 -5
View File
@@ -1,10 +1,10 @@
// auto-generated by elc --emit-header do not edit
// auto-generated by elc --emit-header - do not edit
extern fn agent_person(agent: String) -> String
extern fn agent_number(agent: String) -> String
extern fn realize_np(referent: String, number: String) -> String
extern fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String]
extern fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String
extern fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: Any) -> Any
extern fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: Any) -> String
extern fn capitalize_first(s: String) -> String
extern fn add_punct(s: String, intent: String) -> String
extern fn realize_lang(form: [String], profile: [String]) -> String
extern fn realize(form: [String]) -> String
extern fn realize_lang(form: Any, profile: Any) -> String
extern fn realize(form: Any) -> String
+15 -15
View File
@@ -1,18 +1,18 @@
// auto-generated by elc --emit-header do not edit
extern fn sem_frame(intent: String, subject: String, obj: String, modifiers: String) -> [String]
extern fn sem_frame_lang(intent: String, subject: String, obj: String, modifiers: String, lang_code: String) -> [String]
extern fn sem_frame_simple(intent: String, subject: String) -> [String]
extern fn sem_frame_obj(intent: String, subject: String, obj: String) -> [String]
extern fn sem_intent(frame: [String]) -> String
extern fn sem_subject(frame: [String]) -> String
extern fn sem_object(frame: [String]) -> String
extern fn sem_modifiers(frame: [String]) -> String
extern fn sem_lang(frame: [String]) -> String
// auto-generated by elc --emit-header - do not edit
extern fn sem_frame(intent: String, subject: String, obj: String, modifiers: String) -> Any
extern fn sem_frame_lang(intent: String, subject: String, obj: String, modifiers: String, lang_code: String) -> Any
extern fn sem_frame_simple(intent: String, subject: String) -> Any
extern fn sem_frame_obj(intent: String, subject: String, obj: String) -> Any
extern fn sem_intent(frame: Any) -> String
extern fn sem_subject(frame: Any) -> String
extern fn sem_object(frame: Any) -> String
extern fn sem_modifiers(frame: Any) -> String
extern fn sem_lang(frame: Any) -> String
extern fn sem_first_modifier(mods: String) -> String
extern fn sem_intent_to_realize(intent: String) -> String
extern fn sem_to_spec(frame: [String]) -> [String]
extern fn sem_to_spec_full(frame: [String], verb: String, tense: String, aspect: String) -> [String]
extern fn sem_to_spec(frame: Any) -> Any
extern fn sem_to_spec_full(frame: Any, verb: String, tense: String, aspect: String) -> Any
extern fn sem_realize_greet(subject: String) -> String
extern fn sem_realize(frame: [String]) -> String
extern fn sem_realize_full(frame: [String], verb: String, tense: String, aspect: String) -> String
extern fn sem_realize_lang(frame: [String], lang_code: String) -> String
extern fn sem_realize(frame: Any) -> String
extern fn sem_realize_full(frame: Any, verb: String, tense: String, aspect: String) -> String
extern fn sem_realize_lang(frame: Any, lang_code: String) -> String
+19 -19
View File
@@ -1,20 +1,20 @@
// auto-generated by elc --emit-header do not edit
extern fn lex_word(entry: [String]) -> String
extern fn lex_pos(entry: [String]) -> String
extern fn lex_form(entry: [String], idx: Int) -> String
extern fn lex_class(entry: [String]) -> String
extern fn make_entry(word: String, pos: String, f0: String, f1: String, f2: String, f3: String, f4: String, cls: String) -> [String]
extern fn make_entry2(word: String, pos: String, f0: String, f1: String, cls: String) -> [String]
extern fn make_entry3(word: String, pos: String, f0: String, f1: String, f2: String, cls: String) -> [String]
extern fn make_entry1(word: String, pos: String, f0: String, cls: String) -> [String]
extern fn build_vocab() -> [[String]]
extern fn get_vocab() -> [[String]]
extern fn vocab_lookup(word: String, lang_code: String) -> [String]
extern fn vocab_lookup_en(word: String) -> [String]
// auto-generated by elc --emit-header - do not edit
extern fn lex_word(entry: Any) -> String
extern fn lex_pos(entry: Any) -> String
extern fn lex_form(entry: Any, idx: Int) -> String
extern fn lex_class(entry: Any) -> String
extern fn make_entry(word: String, pos: String, f0: String, f1: String, f2: String, f3: String, f4: String, cls: String) -> Any
extern fn make_entry2(word: String, pos: String, f0: String, f1: String, cls: String) -> Any
extern fn make_entry3(word: String, pos: String, f0: String, f1: String, f2: String, cls: String) -> Any
extern fn make_entry1(word: String, pos: String, f0: String, cls: String) -> Any
extern fn build_vocab() -> Any
extern fn get_vocab() -> Any
extern fn vocab_lookup(word: String, lang_code: String) -> Any
extern fn vocab_lookup_en(word: String) -> Any
extern fn vocab_synonym(word: String, lang_register: String, lang_code: String) -> String
extern fn vocab_by_pos(pos: String) -> [[String]]
extern fn vocab_by_class(cls: String) -> [[String]]
extern fn entry_found(entry: [String]) -> Bool
extern fn entry_word(entry: [String]) -> String
extern fn entry_pos(entry: [String]) -> String
extern fn entry_form(entry: [String], n: Int) -> String
extern fn vocab_by_pos(pos: String) -> Any
extern fn vocab_by_class(cls: String) -> Any
extern fn entry_found(entry: Any) -> Bool
extern fn entry_word(entry: Any) -> String
extern fn entry_pos(entry: Any) -> String
extern fn entry_form(entry: Any, n: Int) -> String
+1 -1
View File
@@ -22,7 +22,7 @@ cd "$(dirname "$0")"
EL_HOME="${EL_HOME:-$(cd ../.. && pwd)/el}"
ELC="${ELC:-${EL_HOME}/dist/platform/elc}"
RUNTIME_DIR="${EL_HOME}/runtime"
RUNTIME_DIR="${EL_HOME}/el-compiler/runtime"
SRC_DIR="$(cd .. && pwd)/src"
if [ ! -x "${ELC}" ]; then
+1 -1
View File
@@ -81,7 +81,7 @@ jobs:
# Link to produce the engram binary
- name: Link engram binary
run: |
cc -std=c11 -O2 -DHAVE_CURL \
cc -std=c11 -O2 \
-I /usr/local/lib/el \
-o dist/engram \
dist/engram.c \
+1 -1
View File
@@ -88,7 +88,7 @@ jobs:
# Link to produce the engram binary
- name: Link engram binary
run: |
cc -std=c11 -O2 -DHAVE_CURL \
cc -std=c11 -O2 \
-I /usr/local/lib/el \
-o dist/engram \
dist/engram.c \
+1 -8
View File
@@ -49,12 +49,6 @@ jobs:
echo "Downloading el_runtime.h..."
curl -fsSL "${RELEASE_BASE}/el_runtime.h" -o /usr/local/lib/el/el_runtime.h
echo "Downloading engram_store.c..."
curl -fsSL "${RELEASE_BASE}/engram_store.c" -o /usr/local/lib/el/engram_store.c
echo "Downloading engram_store.h..."
curl -fsSL "${RELEASE_BASE}/engram_store.h" -o /usr/local/lib/el/engram_store.h
echo "El SDK installed:"
elc --version || true
@@ -68,12 +62,11 @@ jobs:
# Link to produce the engram binary
- name: Link engram binary
run: |
cc -std=c11 -O2 -DHAVE_CURL \
cc -std=c11 -O2 \
-I /usr/local/lib/el \
-o dist/engram \
dist/engram.c \
/usr/local/lib/el/el_runtime.c \
/usr/local/lib/el/engram_store.c \
-lcurl -lpthread
echo "Linked dist/engram"
ls -lh dist/engram
+2 -5
View File
@@ -1,6 +1,3 @@
.DS_Store
*.db
*.elc
*.elh
dist/
target/
*.db
.DS_Store
BIN
View File
Binary file not shown.
+75 -260
View File
@@ -10,9 +10,6 @@ el_val_t query_param(el_val_t path, el_val_t key);
el_val_t query_int(el_val_t path, el_val_t key, el_val_t default_val);
el_val_t extract_id(el_val_t path, el_val_t prefix);
el_val_t route_stats(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_act_stats(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_text_health(el_val_t method, el_val_t path, el_val_t body);
el_val_t persist_canonical(void);
el_val_t route_create_node(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_get_node(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_scan_nodes(el_val_t method, el_val_t path, el_val_t body);
@@ -20,29 +17,19 @@ el_val_t route_scan_edges(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_search(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_activate(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_create_edge(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_create_edges_batch(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_neighbors(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_strengthen(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_forget(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_save(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_load(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_health(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_embed_backfill(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_sync(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_load_merge(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_emit_ise(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_capture_knowledge(el_val_t method, el_val_t path, el_val_t body);
el_val_t route_similarity(el_val_t method, el_val_t path, el_val_t body);
el_val_t check_auth_ok(el_val_t method, el_val_t body);
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);
el_val_t bind_raw;
el_val_t bind_str;
el_val_t port;
el_val_t data_dir_raw;
el_val_t data_dir;
el_val_t snapshot_path;
el_val_t boot_snap;
el_val_t parse_port(el_val_t bind) {
el_val_t colon = str_index_of(bind, EL_STR(":"));
@@ -121,40 +108,17 @@ el_val_t route_stats(el_val_t method, el_val_t path, el_val_t body) {
return 0;
}
el_val_t route_act_stats(el_val_t method, el_val_t path, el_val_t body) {
return engram_act_stats_json();
return 0;
}
el_val_t route_text_health(el_val_t method, el_val_t path, el_val_t body) {
return engram_text_health_json();
return 0;
}
el_val_t persist_canonical(void) {
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_1 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_1 = (EL_STR("/tmp/engram")); } else { _if_result_1 = (dir_raw); } _if_result_1; });
return engram_save(el_str_concat(dir, EL_STR("/snapshot.json")));
return 0;
}
el_val_t route_create_node(el_val_t method, el_val_t path, el_val_t body) {
el_val_t content = json_get_string(body, EL_STR("content"));
el_val_t nt_raw = json_get_string(body, EL_STR("node_type"));
el_val_t node_type = ({ el_val_t _if_result_2 = 0; if (str_eq(nt_raw, EL_STR(""))) { _if_result_2 = (EL_STR("Memory")); } else { _if_result_2 = (nt_raw); } _if_result_2; });
el_val_t sal_present = json_get_raw(body, EL_STR("salience"));
el_val_t salience = ({ el_val_t _if_result_3 = 0; if (str_eq(sal_present, EL_STR(""))) { _if_result_3 = (el_from_float(0.5)); } else { _if_result_3 = (json_get_float(body, EL_STR("salience"))); } _if_result_3; });
el_val_t label_raw = json_get_string(body, EL_STR("label"));
el_val_t label = ({ el_val_t _if_result_4 = 0; if (str_eq(label_raw, EL_STR(""))) { _if_result_4 = (content); } else { _if_result_4 = (label_raw); } _if_result_4; });
el_val_t imp_present = json_get_raw(body, EL_STR("importance"));
el_val_t importance = ({ el_val_t _if_result_5 = 0; if (str_eq(imp_present, EL_STR(""))) { _if_result_5 = (el_from_float(0.5)); } else { _if_result_5 = (json_get_float(body, EL_STR("importance"))); } _if_result_5; });
el_val_t conf_present = json_get_raw(body, EL_STR("confidence"));
el_val_t confidence = ({ el_val_t _if_result_6 = 0; if (str_eq(conf_present, EL_STR(""))) { _if_result_6 = (el_from_float(1.0)); } else { _if_result_6 = (json_get_float(body, EL_STR("confidence"))); } _if_result_6; });
el_val_t tier_raw = json_get_string(body, EL_STR("tier"));
el_val_t tier = ({ el_val_t _if_result_7 = 0; if (str_eq(tier_raw, EL_STR(""))) { _if_result_7 = (EL_STR("Working")); } else { _if_result_7 = (tier_raw); } _if_result_7; });
el_val_t tags = json_get_string(body, EL_STR("tags"));
el_val_t id = engram_node_full(content, node_type, label, salience, importance, confidence, tier, tags);
el_val_t saved = persist_canonical();
el_val_t node_type = json_get_string(body, EL_STR("node_type"));
if (str_eq(node_type, EL_STR(""))) {
node_type = EL_STR("Memory");
}
el_val_t salience = json_get_float(body, EL_STR("salience"));
if (str_eq(salience, el_from_float(0.0))) {
salience = el_from_float(0.5);
}
el_val_t id = engram_node(content, node_type, salience);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"content\":\"")), content), EL_STR("\",\"node_type\":\"")), node_type), EL_STR("\"}"));
return 0;
}
@@ -180,9 +144,11 @@ el_val_t route_scan_nodes(el_val_t method, el_val_t path, el_val_t body) {
}
el_val_t route_scan_edges(el_val_t method, el_val_t path, el_val_t body) {
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_8 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_8 = (EL_STR("/tmp/engram")); } else { _if_result_8 = (dir_raw); } _if_result_8; });
el_val_t snap_path = el_str_concat(dir, EL_STR("/.scan-export.json"));
el_val_t dir = env(EL_STR("ENGRAM_DATA_DIR"));
if (str_eq(dir, EL_STR(""))) {
dir = EL_STR("/tmp/engram");
}
el_val_t snap_path = el_str_concat(dir, EL_STR("/snapshot.json"));
engram_save(snap_path);
el_val_t snap = fs_read(snap_path);
if (str_eq(snap, EL_STR(""))) {
@@ -197,22 +163,36 @@ el_val_t route_scan_edges(el_val_t method, el_val_t path, el_val_t body) {
}
el_val_t route_search(el_val_t method, el_val_t path, el_val_t body) {
el_val_t q = ({ el_val_t _if_result_9 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_9 = (query_param(path, EL_STR("q"))); } else { _if_result_9 = (json_get_string(body, EL_STR("query"))); } _if_result_9; });
el_val_t lim_url = query_int(path, EL_STR("limit"), 0);
el_val_t lim_body = json_get_int(body, EL_STR("limit"));
el_val_t lim_either = ({ el_val_t _if_result_10 = 0; if ((lim_url > 0)) { _if_result_10 = (lim_url); } else { _if_result_10 = (lim_body); } _if_result_10; });
el_val_t limit = ({ el_val_t _if_result_11 = 0; if ((lim_either > 0)) { _if_result_11 = (lim_either); } else { _if_result_11 = (20); } _if_result_11; });
el_val_t q = EL_STR("");
if (str_eq(method, EL_STR("GET"))) {
q = query_param(path, EL_STR("q"));
} else {
q = json_get_string(body, EL_STR("query"));
}
el_val_t limit = query_int(path, EL_STR("limit"), 20);
if (limit == 0) {
limit = json_get_int(body, EL_STR("limit"));
}
if (limit == 0) {
limit = 20;
}
return engram_search_json(q, limit);
return 0;
}
el_val_t route_activate(el_val_t method, el_val_t path, el_val_t body) {
el_val_t q = ({ el_val_t _if_result_12 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_12 = (query_param(path, EL_STR("q"))); } else { _if_result_12 = (json_get_string(body, EL_STR("query"))); } _if_result_12; });
if (str_eq(q, EL_STR(""))) {
return err_json(EL_STR("missing query"));
el_val_t q = EL_STR("");
el_val_t depth = 3;
if (str_eq(method, EL_STR("GET"))) {
q = query_param(path, EL_STR("q"));
depth = query_int(path, EL_STR("depth"), 3);
} else {
q = json_get_string(body, EL_STR("query"));
el_val_t bd = json_get_int(body, EL_STR("depth"));
if (bd > 0) {
depth = bd;
}
}
el_val_t d_raw = ({ el_val_t _if_result_13 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_13 = (query_int(path, EL_STR("depth"), 3)); } else { _if_result_13 = (json_get_int(body, EL_STR("depth"))); } _if_result_13; });
el_val_t depth = ({ el_val_t _if_result_14 = 0; if ((d_raw > 0)) { _if_result_14 = (d_raw); } else { _if_result_14 = (3); } _if_result_14; });
return el_str_concat(el_str_concat(EL_STR("{\"results\":"), engram_activate_json(q, depth)), EL_STR("}"));
return 0;
}
@@ -220,51 +200,19 @@ el_val_t route_activate(el_val_t method, el_val_t path, el_val_t body) {
el_val_t route_create_edge(el_val_t method, el_val_t path, el_val_t body) {
el_val_t from_id = json_get_string(body, EL_STR("from_id"));
el_val_t to_id = json_get_string(body, EL_STR("to_id"));
el_val_t rel_raw = json_get_string(body, EL_STR("relation"));
el_val_t relation = ({ el_val_t _if_result_15 = 0; if (str_eq(rel_raw, EL_STR(""))) { _if_result_15 = (EL_STR("associates")); } else { _if_result_15 = (rel_raw); } _if_result_15; });
el_val_t w_present = json_get_raw(body, EL_STR("weight"));
el_val_t weight = ({ el_val_t _if_result_16 = 0; if (str_eq(w_present, EL_STR(""))) { _if_result_16 = (el_from_float(0.5)); } else { _if_result_16 = (json_get_float(body, EL_STR("weight"))); } _if_result_16; });
el_val_t relation = json_get_string(body, EL_STR("relation"));
if (str_eq(relation, EL_STR(""))) {
relation = EL_STR("associates");
}
el_val_t weight = json_get_float(body, EL_STR("weight"));
if (str_eq(weight, el_from_float(0.0))) {
weight = el_from_float(0.5);
}
engram_connect(from_id, to_id, weight, relation);
el_val_t saved = persist_canonical();
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), relation), EL_STR("\"}"));
return 0;
}
el_val_t route_create_edges_batch(el_val_t method, el_val_t path, el_val_t body) {
el_val_t arr = json_get_raw(body, EL_STR("edges"));
if (str_eq(arr, EL_STR(""))) {
return err_json(EL_STR("missing edges array"));
}
el_val_t n = json_array_len(arr);
if (n == 0) {
return EL_STR("{\"ok\":true,\"accepted\":0,\"skipped\":0}");
}
el_val_t i = 0;
el_val_t accepted = 0;
el_val_t skipped = 0;
while (i < n) {
el_val_t item = json_array_get(arr, i);
el_val_t from_id = json_get_string(item, EL_STR("from_id"));
el_val_t to_id = json_get_string(item, EL_STR("to_id"));
if (str_eq(from_id, EL_STR("")) || str_eq(to_id, EL_STR(""))) {
skipped = (skipped + 1);
} else {
el_val_t rel_raw = json_get_string(item, EL_STR("relation"));
el_val_t relation = ({ el_val_t _if_result_17 = 0; if (str_eq(rel_raw, EL_STR(""))) { _if_result_17 = (EL_STR("associates")); } else { _if_result_17 = (rel_raw); } _if_result_17; });
el_val_t w_present = json_get_raw(item, EL_STR("weight"));
el_val_t weight = ({ el_val_t _if_result_18 = 0; if (str_eq(w_present, EL_STR(""))) { _if_result_18 = (el_from_float(0.5)); } else { _if_result_18 = (json_get_float(item, EL_STR("weight"))); } _if_result_18; });
engram_connect(from_id, to_id, weight, relation);
accepted = (accepted + 1);
}
i = (i + 1);
}
if (accepted > 0) {
el_val_t saved = persist_canonical();
}
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"accepted\":"), int_to_str(accepted)), EL_STR(",\"skipped\":")), int_to_str(skipped)), EL_STR("}"));
return 0;
}
el_val_t route_neighbors(el_val_t method, el_val_t path, el_val_t body) {
el_val_t id = extract_id(path, EL_STR("/api/neighbors/"));
if (str_eq(id, EL_STR(""))) {
@@ -281,7 +229,6 @@ el_val_t route_strengthen(el_val_t method, el_val_t path, el_val_t body) {
return err_json(EL_STR("missing node_id"));
}
engram_strengthen(id);
el_val_t saved = persist_canonical();
return ok_json();
return 0;
}
@@ -292,140 +239,40 @@ el_val_t route_forget(el_val_t method, el_val_t path, el_val_t body) {
return err_json(EL_STR("missing id"));
}
engram_forget(id);
el_val_t saved = persist_canonical();
return ok_json();
return 0;
}
el_val_t route_save(el_val_t method, el_val_t path, el_val_t body) {
el_val_t p_raw = json_get_string(body, EL_STR("path"));
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_19 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_19 = (EL_STR("/tmp/engram")); } else { _if_result_19 = (dir_raw); } _if_result_19; });
el_val_t p = ({ el_val_t _if_result_20 = 0; if (str_eq(p_raw, EL_STR(""))) { _if_result_20 = (el_str_concat(dir, EL_STR("/snapshot.json"))); } else { _if_result_20 = (p_raw); } _if_result_20; });
el_val_t sv = engram_save(p);
el_val_t sv_ok = ({ el_val_t _if_result_21 = 0; if ((sv == 0)) { _if_result_21 = (EL_STR("false")); } else { _if_result_21 = (EL_STR("true")); } _if_result_21; });
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":"), sv_ok), EL_STR(",\"path\":\"")), p), EL_STR("\",\"node_count\":")), int_to_str(engram_node_count())), EL_STR(",\"edge_count\":")), int_to_str(engram_edge_count())), EL_STR("}"));
el_val_t p = json_get_string(body, EL_STR("path"));
if (str_eq(p, EL_STR(""))) {
el_val_t dir = env(EL_STR("ENGRAM_DATA_DIR"));
if (str_eq(dir, EL_STR(""))) {
dir = EL_STR("/tmp/engram");
}
p = el_str_concat(dir, EL_STR("/snapshot.json"));
}
engram_save(p);
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"path\":\""), p), EL_STR("\"}"));
return 0;
}
el_val_t route_load(el_val_t method, el_val_t path, el_val_t body) {
el_val_t p_raw = json_get_string(body, EL_STR("path"));
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_22 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_22 = (EL_STR("/tmp/engram")); } else { _if_result_22 = (dir_raw); } _if_result_22; });
el_val_t p = ({ el_val_t _if_result_23 = 0; if (str_eq(p_raw, EL_STR(""))) { _if_result_23 = (el_str_concat(dir, EL_STR("/snapshot.json"))); } else { _if_result_23 = (p_raw); } _if_result_23; });
el_val_t ld = engram_load(p);
el_val_t ld_ok = ({ el_val_t _if_result_24 = 0; if ((ld == 0)) { _if_result_24 = (EL_STR("false")); } else { _if_result_24 = (EL_STR("true")); } _if_result_24; });
el_val_t nc_after = engram_node_count();
el_val_t hollow = ({ el_val_t _if_result_25 = 0; if ((nc_after == 0)) { _if_result_25 = (EL_STR("true")); } else { _if_result_25 = (EL_STR("false")); } _if_result_25; });
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":"), ld_ok), EL_STR(",\"path\":\"")), p), EL_STR("\",\"node_count\":")), int_to_str(nc_after)), EL_STR(",\"edge_count\":")), int_to_str(engram_edge_count())), EL_STR(",\"hollow\":")), hollow), EL_STR("}"));
el_val_t p = json_get_string(body, EL_STR("path"));
if (str_eq(p, EL_STR(""))) {
el_val_t dir = env(EL_STR("ENGRAM_DATA_DIR"));
if (str_eq(dir, EL_STR(""))) {
dir = EL_STR("/tmp/engram");
}
p = el_str_concat(dir, EL_STR("/snapshot.json"));
}
engram_load(p);
return ok_json();
return 0;
}
el_val_t route_health(el_val_t method, el_val_t path, el_val_t body) {
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"status\":\"ok\",\"engine\":\"engram-runtime-native\",\"node_count\":"), int_to_str(engram_node_count())), EL_STR(",\"edge_count\":")), int_to_str(engram_edge_count())), EL_STR("}"));
return 0;
}
el_val_t route_embed_backfill(el_val_t method, el_val_t path, el_val_t body) {
el_val_t n = query_int(path, EL_STR("n"), 32);
el_val_t result = engram_embed_backfill(n);
el_val_t done = json_get_float(result, EL_STR("embedded"));
if (done > el_from_float(0.0)) {
el_val_t saved = persist_canonical();
}
return result;
return 0;
}
el_val_t route_sync(el_val_t method, el_val_t path, el_val_t body) {
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_26 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_26 = (EL_STR("/tmp/engram")); } else { _if_result_26 = (dir_raw); } _if_result_26; });
el_val_t snap_path = el_str_concat(dir, EL_STR("/.sync-export.json"));
engram_save(snap_path);
el_val_t snap = fs_read(snap_path);
if (str_eq(snap, EL_STR(""))) {
return err_json(EL_STR("sync export failed: snapshot unreadable"));
}
return snap;
return 0;
}
el_val_t route_load_merge(el_val_t method, el_val_t path, el_val_t body) {
el_val_t p = json_get_string(body, EL_STR("path"));
if (str_eq(p, EL_STR(""))) {
return err_json(EL_STR("path is required"));
}
if (str_eq(fs_read(p), EL_STR(""))) {
return err_json(EL_STR("file missing or empty"));
}
el_val_t before_n = engram_node_count();
el_val_t before_e = engram_edge_count();
engram_load_merge(p);
el_val_t added_n = (engram_node_count() - before_n);
el_val_t added_e = (engram_edge_count() - before_e);
el_val_t saved = persist_canonical();
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"nodes_added\":"), int_to_str(added_n)), EL_STR(",\"edges_added\":")), int_to_str(added_e)), EL_STR(",\"node_count\":")), int_to_str(engram_node_count())), EL_STR("}"));
return 0;
}
el_val_t route_emit_ise(el_val_t method, el_val_t path, el_val_t body) {
el_val_t content = json_get_string(body, EL_STR("content"));
if (str_eq(content, EL_STR(""))) {
return err_json(EL_STR("missing content"));
}
el_val_t sal = el_from_float(0.3);
el_val_t imp = el_from_float(0.3);
el_val_t conf = el_from_float(0.8);
el_val_t id = engram_node_full(content, EL_STR("InternalStateEvent"), EL_STR("state-event"), sal, imp, conf, EL_STR("Episodic"), EL_STR("[\"internal-state\",\"InternalStateEvent\"]"));
el_val_t ret_raw = env(EL_STR("ENGRAM_ISE_RETENTION_MS"));
el_val_t ret_ms = ({ el_val_t _if_result_27 = 0; if (str_eq(ret_raw, EL_STR(""))) { _if_result_27 = (172800000); } else { _if_result_27 = (str_to_int(ret_raw)); } _if_result_27; });
el_val_t pruned = engram_prune_telemetry(ret_ms);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\",\"pruned\":")), int_to_str(pruned)), EL_STR("}"));
return 0;
}
el_val_t route_capture_knowledge(el_val_t method, el_val_t path, el_val_t body) {
el_val_t content = json_get_string(body, EL_STR("content"));
if (str_eq(content, EL_STR(""))) {
return err_json(EL_STR("missing content"));
}
el_val_t title = json_get_string(body, EL_STR("title"));
el_val_t label = ({ el_val_t _if_result_28 = 0; if (str_eq(title, EL_STR(""))) { _if_result_28 = (str_slice(content, 0, 60)); } else { _if_result_28 = (title); } _if_result_28; });
el_val_t category_raw = json_get_string(body, EL_STR("category"));
el_val_t category = ({ el_val_t _if_result_29 = 0; if (str_eq(category_raw, EL_STR(""))) { _if_result_29 = (EL_STR("other")); } else { _if_result_29 = (category_raw); } _if_result_29; });
el_val_t ktier_raw = json_get_string(body, EL_STR("tier"));
el_val_t ktier = ({ el_val_t _if_result_30 = 0; if (str_eq(ktier_raw, EL_STR(""))) { _if_result_30 = (EL_STR("note")); } else { _if_result_30 = (ktier_raw); } _if_result_30; });
el_val_t project = json_get_string(body, EL_STR("project"));
el_val_t tags_raw = json_get_raw(body, EL_STR("tags"));
el_val_t tags_base = ({ el_val_t _if_result_31 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_31 = (EL_STR("[]")); } else { _if_result_31 = (tags_raw); } _if_result_31; });
el_val_t base_len = str_len(tags_base);
el_val_t head = str_slice(tags_base, 0, (base_len - 1));
el_val_t sep = ({ el_val_t _if_result_32 = 0; if (str_eq(head, EL_STR("["))) { _if_result_32 = (EL_STR("")); } else { _if_result_32 = (EL_STR(",")); } _if_result_32; });
el_val_t safe_cat = str_replace(category, EL_STR("\""), EL_STR("'"));
el_val_t safe_tier = str_replace(ktier, EL_STR("\""), EL_STR("'"));
el_val_t safe_proj = str_replace(project, EL_STR("\""), EL_STR("'"));
el_val_t proj_tag = ({ el_val_t _if_result_33 = 0; if (str_eq(safe_proj, EL_STR(""))) { _if_result_33 = (EL_STR("")); } else { _if_result_33 = (el_str_concat(el_str_concat(EL_STR(",\"project:"), safe_proj), EL_STR("\""))); } _if_result_33; });
el_val_t tags = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(head, sep), EL_STR("\"category:")), safe_cat), EL_STR("\",\"tier:")), safe_tier), EL_STR("\"")), proj_tag), EL_STR("]"));
el_val_t sal = el_from_float(0.5);
el_val_t imp = el_from_float(0.5);
el_val_t conf = el_from_float(0.9);
el_val_t id = engram_node_full(content, EL_STR("Knowledge"), label, sal, imp, conf, EL_STR("Semantic"), tags);
el_val_t saved = persist_canonical();
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\"}"));
return 0;
}
el_val_t route_similarity(el_val_t method, el_val_t path, el_val_t body) {
el_val_t a = query_param(path, EL_STR("a"));
el_val_t b = query_param(path, EL_STR("b"));
if (str_eq(a, EL_STR(""))) {
return err_json(EL_STR("missing a"));
}
if (str_eq(b, EL_STR(""))) {
return err_json(EL_STR("missing b"));
}
el_val_t sim = engram_cosine_sim(a, b);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"a\":\""), a), EL_STR("\",\"b\":\"")), b), EL_STR("\",\"cosine\":")), float_to_str(sim)), EL_STR("}"));
return EL_STR("{\"status\":\"ok\",\"engine\":\"engram-runtime-native\"}");
return 0;
}
@@ -452,24 +299,12 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
return route_health(method, path, body);
}
}
if (str_eq(method, EL_STR("POST")) && str_eq(clean, EL_STR("/api/neuron/state-events"))) {
return route_emit_ise(method, path, body);
}
if (!check_auth_ok(method, body)) {
return err_json(EL_STR("unauthorized"));
}
if (str_eq(method, EL_STR("POST")) && str_eq(clean, EL_STR("/api/neuron/knowledge/capture"))) {
return route_capture_knowledge(method, path, body);
}
if (str_eq(method, EL_STR("GET")) && (str_eq(clean, EL_STR("/api/stats")) || str_eq(clean, EL_STR("/stats")))) {
return route_stats(method, path, body);
}
if (str_eq(method, EL_STR("GET")) && (str_eq(clean, EL_STR("/api/act-stats")) || str_eq(clean, EL_STR("/act-stats")))) {
return route_act_stats(method, path, body);
}
if (str_eq(method, EL_STR("GET")) && (str_eq(clean, EL_STR("/api/text-health")) || str_eq(clean, EL_STR("/text-health")))) {
return route_text_health(method, path, body);
}
if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/nodes")) || str_eq(clean, EL_STR("/nodes")))) {
return route_create_node(method, path, body);
}
@@ -488,9 +323,6 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/edges")) || str_eq(clean, EL_STR("/edges")))) {
return route_create_edge(method, path, body);
}
if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/edges/batch")) || str_eq(clean, EL_STR("/edges/batch")))) {
return route_create_edges_batch(method, path, body);
}
if (str_eq(method, EL_STR("GET")) && str_starts_with(clean, EL_STR("/api/neighbors/"))) {
return route_neighbors(method, path, body);
}
@@ -515,40 +347,23 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/load")) || str_eq(clean, EL_STR("/load")))) {
return route_load(method, path, body);
}
if (str_eq(method, EL_STR("POST")) && (str_eq(clean, EL_STR("/api/load-merge")) || str_eq(clean, EL_STR("/load-merge")))) {
return route_load_merge(method, path, body);
}
if (str_eq(method, EL_STR("GET")) && str_eq(clean, EL_STR("/api/sync"))) {
return route_sync(method, path, body);
}
if (str_eq(clean, EL_STR("/api/embed-backfill"))) {
return route_embed_backfill(method, path, body);
}
if (str_eq(method, EL_STR("GET")) && str_starts_with(clean, EL_STR("/api/similarity"))) {
return route_similarity(method, path, body);
}
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"not found\",\"path\":\""), clean), EL_STR("\"}"));
return 0;
}
int main(int _argc, char** _argv) {
el_runtime_init_args(_argc, _argv);
bind_raw = env(EL_STR("ENGRAM_BIND"));
bind_str = ({ el_val_t _if_result_34 = 0; if (str_eq(bind_raw, EL_STR(""))) { _if_result_34 = (EL_STR(":8742")); } else { _if_result_34 = (bind_raw); } _if_result_34; });
bind_str = env(EL_STR("ENGRAM_BIND"));
if (str_eq(bind_str, EL_STR(""))) {
bind_str = EL_STR(":8742");
}
port = parse_port(bind_str);
data_dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
data_dir = ({ el_val_t _if_result_35 = 0; if (str_eq(data_dir_raw, EL_STR(""))) { _if_result_35 = (EL_STR("/tmp/engram")); } else { _if_result_35 = (data_dir_raw); } _if_result_35; });
data_dir = env(EL_STR("ENGRAM_DATA_DIR"));
if (str_eq(data_dir, EL_STR(""))) {
data_dir = EL_STR("/tmp/engram");
}
snapshot_path = el_str_concat(data_dir, EL_STR("/snapshot.json"));
engram_load(snapshot_path);
boot_snap = fs_read(snapshot_path);
if (!str_eq(boot_snap, EL_STR(""))) {
if (engram_node_count() == 0) {
println(EL_STR("[engram] WARNING: snapshot.json is non-empty but load produced 0 nodes \xe2\x80\x94 preserving copy at snapshot.failed-load.json"));
fs_write(el_str_concat(data_dir, EL_STR("/snapshot.failed-load.json")), boot_snap);
} else {
fs_write(el_str_concat(data_dir, EL_STR("/snapshot.boot-backup.json")), boot_snap);
}
}
println(EL_STR("[engram] runtime-native graph engine"));
println(el_str_concat(EL_STR("[engram] data_dir="), data_dir));
println(el_str_concat(EL_STR("[engram] node_count="), int_to_str(engram_node_count())));
+58 -603
View File
@@ -76,182 +76,13 @@ fn route_stats(method: String, path: String, body: String) -> String {
engram_stats_json()
}
// route_act_stats GET /api/act-stats
// (2026-08-04 self-review) engram_act_stats_json() has existed since the
// 2026-07-27 review but was reachable ONLY through the soul daemon's heartbeat
// binding. Every activation-layer gauge WM evictions, breakthroughs, embedder
// breaker state, context drift, and now the Hebbian counters was therefore
// invisible unless the soul happened to be running and its ISEs were read back
// out of the store. Diagnosing the activation layer required a working soul,
// which is exactly backwards: the lower layer should be observable on its own.
// This review needed it to verify link formation and could not get at it. One
// line of plumbing, and the whole activation layer becomes directly diagnosable.
fn route_act_stats(method: String, path: String, body: String) -> String {
engram_act_stats_json()
}
// route_text_health GET /api/text-health
// (2026-08-08 self-review) The daily census half of the text-integrity gauge.
// Today's review found that the JSON parser had been replacing every \uXXXX
// escape with a literal '?' for at least two months: 3,119 of 4,081
// non-telemetry nodes (76%) were damaged, including the self traversal root
// and every values node, and NOTHING detected it because every gauge in the
// system measured whether the machinery was running, and none measured whether
// the text it carried was intact. No snapshot on disk predates the damage, so
// it cannot be undone; it can only be made impossible to repeat quietly.
//
// The parser is fixed. This route is the standing check: `damaged` should now
// hold flat at its historical floor and never climb. `write_damaged` (also on
// the heartbeat as txt_damaged) is the live regression signal non-zero means
// a write path is mangling text right now.
fn route_text_health(method: String, path: String, body: String) -> String {
engram_text_health_json()
}
// (2026-07-18 self-review) Scoping sweep: `let` inside an if-block creates an
// inner scope only it does NOT mutate the outer binding (documented with
// evidence in awareness.el, 2026-05-25). Every default/reassignment below used
// that broken pattern, so defaults never applied: nodes were created with
// node_type="" and salience=0.0, /api/search and /api/activate ALWAYS ran with
// q="" regardless of input, edges defaulted to relation=""/weight=0.0, and
// save/load with no "path" hit engram_save(""). Rewritten to the
// `let x = if cond { a } else { b }` expression form (the pattern the newer
// routes route_emit_ise/route_capture_knowledge already use correctly).
// store_on ENGRAM_STORE flag (tiered paged store as the durable owner). Matches
// engram_store_enabled() in el_runtime.c EXACTLY (1 / on / true). Default off
// every persistence path below is byte-for-byte the historical snapshot behavior.
fn store_on() -> Bool {
let v: String = env("ENGRAM_STORE")
if str_eq(v, "1") { return true }
if str_eq(v, "on") { return true }
if str_eq(v, "true") { return true }
return false
}
// persist_canonical save the canonical snapshot after a durable write.
//
// WHY (2026-07-22 self-review): the 2026-07-21 fix correctly stopped READ
// routes from writing the canonical snapshot.json but nothing was left
// that saved it on WRITE. Every mutation (node create, edge create,
// knowledge capture, forget, merge) lived only in RAM until someone POSTed
// /api/save manually; a process restart silently discarded everything since
// the last manual save. Observed live: two engram restarts during the
// 2026-07-22 review reverted the store to a ~17h-old snapshot, destroying
// same-day writes. Reads must never write the canonical; writes must always
// persist it. ISE telemetry is deliberately excluded (48h-pruned, loss-
// tolerant, ~2/min snapshotting the whole store per heartbeat is waste;
// any durable write that follows persists the pruning too).
fn persist_canonical() -> Int {
// ENGRAM_STORE: the paged store is the durable owner a checkpoint flushes
// dirty pages behind a WAL-durable record (durable the moment the WAL fsyncs).
// This is the fix for the "restart reverted to a 17h-old snapshot" data loss:
// durable writes no longer depend on a full snapshot.json rewrite. Returns 1
// on a successful checkpoint, 0 otherwise. Flag-off: unchanged (writes JSON).
if store_on() {
return engram_store_checkpoint()
}
let dir_raw: String = env("ENGRAM_DATA_DIR")
let dir: String = engram_resolve_data_dir()
// (2026-08-10 self-review) This returned a hardcoded 1, which made every
// caller's `let saved: Int = persist_canonical()` a dead variable six
// durable write paths each believed they had confirmation of a successful
// canonical persist and none of them had any. Propagate the real result.
return engram_save(dir + "/snapshot.json")
}
// WAL persistence (design doc §§3-14; gated behind ENGRAM_WAL=on) ──────────
// Default OFF every persist path below is byte-identical to the historical
// per-write full-snapshot behavior. When ON, structural mutations append O(1)
// WAL records instead of rewriting the whole graph, with threshold compaction.
fn wal_on() -> Bool {
str_eq(env("ENGRAM_WAL"), "on")
}
// Persist a single-node mutation (create / content-evolve / strengthen).
fn persist_node(id: String) -> Int {
if wal_on() {
let d: String = engram_resolve_data_dir()
let a: Int = engram_wal_node_put(d, id)
let c: Int = engram_wal_maybe_compact(d)
return a
}
return persist_canonical()
}
// Persist edges appended at index >= start (covers single-edge and batch).
fn persist_edges_since(start: Int) -> Int {
if wal_on() {
let d: String = engram_resolve_data_dir()
let a: Int = engram_wal_edges_since(d, start)
let c: Int = engram_wal_maybe_compact(d)
return a
}
return persist_canonical()
}
// Persist a Hebbian consolidation batch as ONE WAL record (single fsync, §5-B).
fn persist_hebb_batch(start: Int) -> Int {
if wal_on() {
let d: String = engram_resolve_data_dir()
let a: Int = engram_wal_hebb_batch(d, start)
let c: Int = engram_wal_maybe_compact(d)
return a
}
return persist_canonical()
}
// Bulk mutation (embedding backfill, load-merge): write a fresh compaction base
// so the many-node change is durable in one atomic snapshot; WAL is truncated.
fn persist_bulk() -> Int {
if wal_on() {
let d: String = engram_resolve_data_dir()
return engram_wal_compact(d)
}
return persist_canonical()
}
// INCOMPLETE-ROUTE FIX (2026-07-24 self-review): this route silently dropped
// label, importance, tier, and tags engram_node() defaults label to content
// and importance to 0.5, so every node created over HTTP lost its metadata.
// Observed live: the soul's boot-counter write-back landed with
// label="soul:boot_count:99" (content), importance 0.5, no tags. Honor the
// full field set via engram_node_full when any of them is supplied.
// PRESENCE-AWARE DEFAULTS (2026-08-01 self-review): the old pattern
// `if x == 0.0 { default }` made a legitimate 0.0 unrepresentable a caller
// setting salience/importance/weight to zero silently got 0.5. json_get_raw
// returns "" when the key is ABSENT and the raw token when present, so
// absence and zero are now distinguishable. Also: confidence was hardcoded
// to 1.0 regardless of input every HTTP-created node claimed full
// epistemic confidence. Now honored from the payload (default 1.0).
fn route_create_node(method: String, path: String, body: String) -> String {
let content: String = json_get_string(body, "content")
let nt_raw: String = json_get_string(body, "node_type")
let node_type: String = if str_eq(nt_raw, "") { "Memory" } else { nt_raw }
let sal_present: String = json_get_raw(body, "salience")
let salience: Float = if str_eq(sal_present, "") { 0.5 } else { json_get_float(body, "salience") }
let label_raw: String = json_get_string(body, "label")
let label: String = if str_eq(label_raw, "") { content } else { label_raw }
let imp_present: String = json_get_raw(body, "importance")
let importance: Float = if str_eq(imp_present, "") { 0.5 } else { json_get_float(body, "importance") }
let conf_present: String = json_get_raw(body, "confidence")
let confidence: Float = if str_eq(conf_present, "") { 1.0 } else { json_get_float(body, "confidence") }
let tier_raw: String = json_get_string(body, "tier")
let tier: String = if str_eq(tier_raw, "") { "Working" } else { tier_raw }
let tags: String = json_get_string(body, "tags")
// NO el_from_float WRAPPER (2026-08-01 self-review): salience/importance/
// confidence are already Float (el_val_t) values json_get_float and
// Float literals both encode. Wrapping them in el_from_float AGAIN
// reinterpreted the boxed bits as a raw double, producing garbage that
// failed engram_decode_score's range check and clamped every HTTP-created
// node to defaults (salience 0.9 in 0.5 stored; confidence 0.6 in → 1.0
// stored verified live). route_emit_ise always passed Floats bare and
// its 0.3/0.3/0.8 stored correctly; this call now does the same.
let id: String = engram_node_full(
content, node_type, label,
salience, importance, confidence,
tier, tags
)
let saved: Int = persist_node(id)
let node_type: String = json_get_string(body, "node_type")
if str_eq(node_type, "") { let node_type = "Memory" }
let salience: Float = json_get_float(body, "salience")
if salience == 0.0 { let salience = 0.5 }
let id: String = engram_node(content, node_type, salience)
"{\"id\":\"" + id + "\",\"content\":\"" + content + "\",\"node_type\":\"" + node_type + "\"}"
}
@@ -272,14 +103,13 @@ fn route_scan_nodes(method: String, path: String, body: String) -> String {
}
// route_scan_edges bulk export of all edges as a JSON array. Implemented
// via engram_save fs_read of a SCRATCH export path. (2026-07-21 self-review:
// previously this saved over the canonical snapshot.json on every GET if the
// process ever booted with a partial/empty store, the first read request
// clobbered the good snapshot. Read routes must never write the canonical path.)
// via engram_save fs_read of the canonical on-disk snapshot, which the
// runtime keeps in lockstep with the in-memory graph. Live against the
// running graph, not a stale export.
fn route_scan_edges(method: String, path: String, body: String) -> String {
let dir_raw: String = env("ENGRAM_DATA_DIR")
let dir: String = engram_resolve_data_dir()
let snap_path: String = dir + "/.scan-export.json"
let dir: String = env("ENGRAM_DATA_DIR")
if str_eq(dir, "") { let dir = "/tmp/engram" }
let snap_path: String = dir + "/snapshot.json"
engram_save(snap_path)
let snap: String = fs_read(snap_path)
if str_eq(snap, "") { return "[]" }
@@ -292,90 +122,43 @@ fn route_scan_edges(method: String, path: String, body: String) -> String {
}
fn route_search(method: String, path: String, body: String) -> String {
let q: String = if str_eq(method, "GET") { query_param(path, "q") } else { json_get_string(body, "query") }
let lim_url: Int = query_int(path, "limit", 0)
let lim_body: Int = json_get_int(body, "limit")
let lim_either: Int = if lim_url > 0 { lim_url } else { lim_body }
let limit: Int = if lim_either > 0 { lim_either } else { 20 }
let q: String = ""
if str_eq(method, "GET") {
let q = query_param(path, "q")
} else {
let q = json_get_string(body, "query")
}
let limit: Int = query_int(path, "limit", 20)
if limit == 0 { let limit = json_get_int(body, "limit") }
if limit == 0 { let limit = 20 }
return engram_search_json(q, limit)
}
fn route_activate(method: String, path: String, body: String) -> String {
let q: String = if str_eq(method, "GET") { query_param(path, "q") } else { json_get_string(body, "query") }
// Guard: engram_activate with an empty query matches zero seeds, which
// zeroes ALL carried working-memory weights (documented in awareness.el
// perceive()). Never let an empty activation through to wipe WM.
if str_eq(q, "") { return err_json("missing query") }
let d_raw: Int = if str_eq(method, "GET") { query_int(path, "depth", 3) } else { json_get_int(body, "depth") }
let depth: Int = if d_raw > 0 { d_raw } else { 3 }
let q: String = ""
let depth: Int = 3
if str_eq(method, "GET") {
let q = query_param(path, "q")
let depth = query_int(path, "depth", 3)
} else {
let q = json_get_string(body, "query")
let bd: Int = json_get_int(body, "depth")
if bd > 0 { let depth = bd }
}
return "{\"results\":" + engram_activate_json(q, depth) + "}"
}
fn route_create_edge(method: String, path: String, body: String) -> String {
let from_id: String = json_get_string(body, "from_id")
let to_id: String = json_get_string(body, "to_id")
let rel_raw: String = json_get_string(body, "relation")
let relation: String = if str_eq(rel_raw, "") { "associates" } else { rel_raw }
// Presence-aware (2026-08-01): weight 0.0 is a legitimate edge weight
// (dormant association); only default when the key is absent.
let w_present: String = json_get_raw(body, "weight")
let weight: Float = if str_eq(w_present, "") { 0.5 } else { json_get_float(body, "weight") }
let ec0: Int = engram_edge_count()
let relation: String = json_get_string(body, "relation")
if str_eq(relation, "") { let relation = "associates" }
let weight: Float = json_get_float(body, "weight")
if weight == 0.0 { let weight = 0.5 }
engram_connect(from_id, to_id, weight, relation)
let saved: Int = persist_edges_since(ec0)
"{\"ok\":true,\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + relation + "\"}"
}
// route_create_edges_batch POST /api/edges/batch {"edges":[{from_id,to_id,relation,weight}, ...]}
//
// WHY THIS EXISTS (2026-08-07 self-review). persist_canonical() writes the
// FULL canonical snapshot 60MB at current graph size and route_create_edge
// calls it once per edge. That is correct for the interactive one-edge case and
// ruinous for any bulk write: the soul's Hebbian consolidation path delivers
// ~14 associations per 8-minute heartbeat, which through the single-edge route
// would be ~840MB of disk writes per beat, ~150GB/day, to persist 14 edges.
//
// The fix is not to weaken durability it is to make the unit of durability
// the BATCH. Connect every edge, then snapshot exactly once. Same guarantee
// (nothing acknowledged is lost to a restart), 1/N the writes. Empty or
// malformed entries are skipped rather than aborting the batch: a consolidation
// payload is best-effort by design, and one bad id should not cost the other 13.
//
// Returns the accepted count so the caller can tell delivery from silence.
fn route_create_edges_batch(method: String, path: String, body: String) -> String {
let arr: String = json_get_raw(body, "edges")
if str_eq(arr, "") { return err_json("missing edges array") }
let n: Int = json_array_len(arr)
if n == 0 { return "{\"ok\":true,\"accepted\":0,\"skipped\":0}" }
let ec0: Int = engram_edge_count()
let i: Int = 0
let accepted: Int = 0
let skipped: Int = 0
while i < n {
let item: String = json_array_get(arr, i)
let from_id: String = json_get_string(item, "from_id")
let to_id: String = json_get_string(item, "to_id")
if str_eq(from_id, "") || str_eq(to_id, "") {
let skipped = skipped + 1
} else {
let rel_raw: String = json_get_string(item, "relation")
let relation: String = if str_eq(rel_raw, "") { "associates" } else { rel_raw }
let w_present: String = json_get_raw(item, "weight")
let weight: Float = if str_eq(w_present, "") { 0.5 } else { json_get_float(item, "weight") }
engram_connect(from_id, to_id, weight, relation)
let accepted = accepted + 1
}
let i = i + 1
}
// ONE snapshot for the whole batch the entire point of this route.
// Skip it when nothing was accepted: an all-malformed payload must not
// trigger a 60MB write.
if accepted > 0 {
let saved: Int = persist_hebb_batch(ec0)
}
return "{\"ok\":true,\"accepted\":" + int_to_str(accepted) + ",\"skipped\":" + int_to_str(skipped) + "}"
}
fn route_neighbors(method: String, path: String, body: String) -> String {
let id: String = extract_id(path, "/api/neighbors/")
if str_eq(id, "") { return err_json("missing id") }
@@ -387,290 +170,40 @@ fn route_strengthen(method: String, path: String, body: String) -> String {
let id: String = json_get_string(body, "node_id")
if str_eq(id, "") { return err_json("missing node_id") }
engram_strengthen(id)
let saved: Int = persist_node(id)
ok_json()
}
// route_forget DELETE /api/nodes/:id INTEGRITY HARDENED (design doc §18.1).
//
// Two invariants now enforced AT THE STORE (not one layer up in neuron-api.el,
// which a direct HTTP client could bypass):
// 1. Write-protection: protected identity/value nodes (derived from the self
// graph self root + values hub + their neighbors, §18.3) cannot be
// deleted over HTTP. Returns 403, node untouched.
// 2. No hard delete over the wire, ever: an ordinary delete creates a
// Tombstone marker node + `tombstones` edge and KEEPS the original node
// and its edges (recoverable), instead of the old destructive
// engram_forget() shift-delete. Raw engram_forget is now internal-GC only
// and no longer reachable from any HTTP route.
fn route_forget(method: String, path: String, body: String) -> String {
let id: String = extract_id(path, "/api/nodes/")
if str_eq(id, "") { return err_json("missing id") }
if engram_is_protected(id) == 1 {
return "{\"__status__\":403,\"error\":\"protected node; deletion refused\",\"id\":\"" + id + "\"}"
}
let tomb_id: String = engram_node_full(
"tombstone:" + id, "Tombstone", "tombstone:" + id,
0.1, 0.1, 1.0, "Episodic", "[\"tombstone\"]"
)
let ec0: Int = engram_edge_count()
engram_connect(tomb_id, id, 1.0, "tombstones")
let saved: Int = if wal_on() {
let d: String = engram_resolve_data_dir()
let a: Int = engram_wal_node_put(d, tomb_id)
let b: Int = engram_wal_edges_since(d, ec0)
let c: Int = engram_wal_maybe_compact(d)
a
} else {
persist_canonical()
}
"{\"ok\":true,\"tombstoned\":\"" + id + "\",\"tombstone_id\":\"" + tomb_id + "\"}"
engram_forget(id)
ok_json()
}
fn route_save(method: String, path: String, body: String) -> String {
let p_raw: String = json_get_string(body, "path")
let dir_raw: String = env("ENGRAM_DATA_DIR")
let dir: String = engram_resolve_data_dir()
let p: String = if str_eq(p_raw, "") { dir + "/snapshot.json" } else { p_raw }
// (2026-08-10 self-review) engram_save returns 0 on an empty path and the
// route discarded it, so the response was a literal "ok":true regardless
// of whether anything was written. Report the actual result AND the counts
// that were supposed to have been written the same move that made
// route_health honest on 2026-08-01. A caller can now tell "saved 13k
// nodes" from "saved nothing and said ok".
let sv: Int = engram_save(p)
let sv_ok: String = if sv == 0 { "false" } else { "true" }
"{\"ok\":" + sv_ok + ",\"path\":\"" + p + "\",\"node_count\":" + int_to_str(engram_node_count()) + ",\"edge_count\":" + int_to_str(engram_edge_count()) + "}"
let p: String = json_get_string(body, "path")
if str_eq(p, "") {
let dir: String = env("ENGRAM_DATA_DIR")
if str_eq(dir, "") { let dir = "/tmp/engram" }
let p = dir + "/snapshot.json"
}
engram_save(p)
"{\"ok\":true,\"path\":\"" + p + "\"}"
}
fn route_load(method: String, path: String, body: String) -> String {
let p_raw: String = json_get_string(body, "path")
let dir_raw: String = env("ENGRAM_DATA_DIR")
let dir: String = engram_resolve_data_dir()
let p: String = if str_eq(p_raw, "") { dir + "/snapshot.json" } else { p_raw }
// (2026-08-10 self-review) This was a stub response over the single most
// destructive operation in the server. engram_load returns 0 on an empty
// path, an unopenable file, a zero-length file, or malloc failure and
// this route answered ok_json() in every one of those cases.
//
// Precise failure shape (el_runtime.c:9890): the fopen guard runs BEFORE
// the store reset, so a MISSING path is genuinely safe it returns 0 with
// the graph intact. The dangerous case is a readable-but-malformed file:
// the reset loop frees every node and edge FIRST, then parses, so a
// truncated or non-snapshot JSON leaves a hollow store and the caller
// was told "ok":true. With 37 GB of stale dated snapshots sitting in the
// data dir as tempting restore targets, "restore reported success and
// silently emptied the graph" is a live risk, not a hypothetical one.
//
// Fix: surface the return value AND the resulting counts. node_count=0
// after a load is the unambiguous hollow-store signal (same convention
// route_health adopted 2026-08-01). Callers can now verify a restore
// instead of trusting it.
let ld: Int = engram_load(p)
let ld_ok: String = if ld == 0 { "false" } else { "true" }
let nc_after: Int = engram_node_count()
let hollow: String = if nc_after == 0 { "true" } else { "false" }
"{\"ok\":" + ld_ok + ",\"path\":\"" + p + "\",\"node_count\":" + int_to_str(nc_after) + ",\"edge_count\":" + int_to_str(engram_edge_count()) + ",\"hollow\":" + hollow + "}"
}
// (2026-08-01 self-review) Health previously returned a hardcoded literal
// it reported "ok" even when the snapshot failed to load and the store was
// empty. Now reports live counts so a monitor can distinguish "up and
// loaded" from "up and hollow" (node_count=0 after boot = failed load).
fn route_health(method: String, path: String, body: String) -> String {
"{\"status\":\"ok\",\"engine\":\"engram-runtime-native\",\"node_count\":" + int_to_str(engram_node_count()) + ",\"edge_count\":" + int_to_str(engram_edge_count()) + "}"
}
// route_embed_backfill GET/POST /api/embed-backfill?n=48
//
// (2026-07-25 self-review) The lazy embedding backfill runs only inside
// engram_activate, and nothing in production calls /api/activate on this
// store the soul's curiosity loop activates its own in-process graph.
// After a restart from a snapshot without vectors, embedded_count stalled
// at 93/12175 and would never recover. This route lets the soul's
// heartbeat pump the backfill explicitly (48/min clears a 12k backlog in
// ~4h). Persists the canonical snapshot whenever new vectors were
// generated the 2026-07-25 regression happened precisely because 3747
// in-RAM embeddings were never snapshotted before a restart. Self-
// limiting: once coverage is full, embedded=0 and no save occurs.
fn route_embed_backfill(method: String, path: String, body: String) -> String {
let n: Int = query_int(path, "n", 32)
let result: String = engram_embed_backfill(n)
let done: Float = json_get_float(result, "embedded")
if done > 0.0 {
let saved: Int = persist_bulk()
}
return result
}
// route_sync return a snapshot of non-ISE/non-Working nodes for the soul daemon
// to merge into its in-process graph via engram_load_merge.
//
// The soul calls GET /api/sync every SOUL_REFRESH_MS (default 10 min) to pull
// new Knowledge/Memory/BacklogItem nodes from the authoritative HTTP Engram into
// its in-process working store. Previously this returned 404 "not found", causing
// the soul to write the error JSON to a temp file and attempt an empty merge.
//
// Strategy: save the current snapshot to disk, read it back, return the full
// snapshot JSON. The soul's engram_load_merge handles large files gracefully
// (it skips nodes already present by ID). Auth-exempt: same-host internal call.
// (2026-06-27 self-review: added this route to fix silent 10-min sync failures)
fn route_sync(method: String, path: String, body: String) -> String {
let dir_raw: String = env("ENGRAM_DATA_DIR")
let dir: String = engram_resolve_data_dir()
// 2026-07-21 self-review: export to a scratch path, never the canonical
// snapshot.json read routes must not be able to clobber the good snapshot.
let snap_path: String = dir + "/.sync-export.json"
engram_save(snap_path)
let snap: String = fs_read(snap_path)
// 2026-08-02 self-review: this used to return {"nodes":[],"edges":[]} when
// the export/read failed. The soul's sync_ok test (awareness.el) only
// checks for "" and "{}", so that placeholder PASSED as a healthy sync:
// soul.last_sync_ok_ts got stamped, sync_age_ms stayed green, the
// sync_empty warn ISE never fired, and engram_sync reported added:0
// forever. A totally broken sync was indistinguishable from a quiet
// healthy one the exact failure class this route was added to fix in
// the first place (see 2026-06-27 note above). Return a real error so the
// failure is loud on both sides.
if str_eq(snap, "") { return err_json("sync export failed: snapshot unreadable") }
return snap
}
// route_load_merge POST /api/load-merge {"path": "..."} merge a snapshot
// file into the live store WITHOUT resetting it (engram_load_merge skips nodes
// already present by id). Added 2026-07-21 self-review to restore the 244 kn-
// identity Knowledge nodes lost from the snapshot lineage between 05-13 and
// 07-13. Requires an explicit path: refuses to run without one so it can never
// be triggered accidentally against a default.
fn route_load_merge(method: String, path: String, body: String) -> String {
let p: String = json_get_string(body, "path")
if str_eq(p, "") { return err_json("path is required") }
if str_eq(fs_read(p), "") { return err_json("file missing or empty") }
let before_n: Int = engram_node_count()
let before_e: Int = engram_edge_count()
engram_load_merge(p)
let added_n: Int = engram_node_count() - before_n
let added_e: Int = engram_edge_count() - before_e
let saved: Int = persist_bulk()
"{\"ok\":true,\"nodes_added\":" + int_to_str(added_n) + ",\"edges_added\":" + int_to_str(added_e) + ",\"node_count\":" + int_to_str(engram_node_count()) + "}"
if str_eq(p, "") {
let dir: String = env("ENGRAM_DATA_DIR")
if str_eq(dir, "") { let dir = "/tmp/engram" }
let p = dir + "/snapshot.json"
}
engram_load(p)
ok_json()
}
// route_emit_ise write an InternalStateEvent node from the soul daemon.
//
// Endpoint: POST /api/neuron/state-events
// Body: {"content": "<json-string>"}
//
// Auth: exempt (internal endpoint, soul daemon on same host, no _auth needed).
// The soul's ise_post() sends {"content":"..."} without _auth; enforcing auth
// here would silently drop all heartbeat/curiosity ISEs. Unauthenticated POST
// to this endpoint is acceptable: ISE writes are observability-only, append-only,
// and come from a trusted process on localhost.
//
// Salience/importance set to match engram_node_full ISE defaults used by the
// in-process fallback path in awareness.el (salience=0.3, importance=0.3,
// confidence=0.8, tier=Episodic).
// (2026-06-26 self-review: added this route after discovering ise_post was
// silently failing the soul posts here but the endpoint didn't exist.)
//
// Retention (2026-07-16 self-review): an earlier comment here claimed ISEs
// got temporal_decay_rate=1.617 that was never implemented (engram_node_full
// hardcodes 0.0), and per-node decay only dampens activation anyway; it never
// removes nodes. By 2026-07-16 ISEs were 75% of the store (10,175 of 13,522
// nodes, ~4,300/day, unbounded). ISEs are already WM-excluded in
// engram_activate, so the fix is retention, not decay: every insert calls
// engram_prune_telemetry(), a single O(nodes+edges) compaction pass that
// removes ISEs older than ENGRAM_ISE_RETENTION_MS (default 48h), protecting
// "session-start" labels and self_review events as durable history. At
// ~3 ISEs/min this bounds telemetry at ~8.6k nodes instead of growing forever.
fn route_emit_ise(method: String, path: String, body: String) -> String {
let content: String = json_get_string(body, "content")
if str_eq(content, "") { return err_json("missing content") }
let sal: Float = 0.3
let imp: Float = 0.3
let conf: Float = 0.8
let id: String = engram_node_full(
content, "InternalStateEvent", "state-event",
sal, imp, conf,
"Episodic", "[\"internal-state\",\"InternalStateEvent\"]"
)
let ret_raw: String = env("ENGRAM_ISE_RETENTION_MS")
let ret_ms: Int = if str_eq(ret_raw, "") { 172800000 } else { str_to_int(ret_raw) }
let pruned: Int = engram_prune_telemetry(ret_ms)
"{\"ok\":true,\"id\":\"" + id + "\",\"pruned\":" + int_to_str(pruned) + "}"
}
// Knowledge capture
//
// route_capture_knowledge direct Knowledge-node capture over HTTP.
//
// Endpoint: POST /api/neuron/knowledge/capture (auth required: "_auth" in body)
// Body: {"content": "...", "title": "...", "category": "...",
// "tier": "note|lesson|canonical", "tags": [...], "project": "...",
// "_auth": "<key>"}
//
// WHY (2026-07-15 self-review): the world-ingestor integrator was designed
// against this endpoint (its MCP-unavailable fallback), but the route never
// existed every direct push 404'd, and because the auth gate ran before
// routing, the failure surfaced as {"error":"unauthorized"} and was
// misdiagnosed for two weeks while world knowledge silently dropped.
// POST /api/nodes was no substitute: it discards label/tags/tier, which
// makes captured knowledge invisible to tag-scoped search and curiosity.
//
// The incoming knowledge tier (note/lesson/canonical) is preserved as a
// "tier:<x>" tag rather than mapped onto Engram's cognitive tiers Knowledge
// nodes land in Semantic (stable reference), and the epistemic tier stays
// queryable without inventing a lossy mapping.
fn route_capture_knowledge(method: String, path: String, body: String) -> String {
let content: String = json_get_string(body, "content")
if str_eq(content, "") { return err_json("missing content") }
let title: String = json_get_string(body, "title")
let label: String = if str_eq(title, "") { str_slice(content, 0, 60) } else { title }
let category_raw: String = json_get_string(body, "category")
let category: String = if str_eq(category_raw, "") { "other" } else { category_raw }
let ktier_raw: String = json_get_string(body, "tier")
let ktier: String = if str_eq(ktier_raw, "") { "note" } else { ktier_raw }
let project: String = json_get_string(body, "project")
let tags_raw: String = json_get_raw(body, "tags")
let tags_base: String = if str_eq(tags_raw, "") { "[]" } else { tags_raw }
// Merge category/tier/project markers into the tag array. Search matches
// against the tags string, so these make captures findable by facet.
let base_len: Int = str_len(tags_base)
let head: String = str_slice(tags_base, 0, base_len - 1)
let sep: String = if str_eq(head, "[") { "" } else { "," }
let safe_cat: String = str_replace(category, "\"", "'")
let safe_tier: String = str_replace(ktier, "\"", "'")
let safe_proj: String = str_replace(project, "\"", "'")
let proj_tag: String = if str_eq(safe_proj, "") { "" } else { ",\"project:" + safe_proj + "\"" }
let tags: String = head + sep + "\"category:" + safe_cat + "\",\"tier:" + safe_tier + "\"" + proj_tag + "]"
let sal: Float = 0.5
let imp: Float = 0.5
let conf: Float = 0.9
let id: String = engram_node_full(
content, "Knowledge", label,
sal, imp, conf,
"Semantic", tags
)
let saved: Int = persist_node(id)
"{\"ok\":true,\"id\":\"" + id + "\"}"
}
// route_similarity GET /api/similarity?a=<id>&b=<id>
//
// (2026-08-01 self-review) engram_cosine_sim was added 2026-07-24
// (bl-b2d1c944) with the stated purpose of exposing semantic distance to
// "EL code and the introspection API" but it had ZERO callers anywhere:
// no route, no soul-daemon use. The activation path uses embeddings
// internally (semantic seeding, Pass-2 additive term), but there was no way
// to probe pairwise node similarity from outside. This closes that: cosine
// in [-1,1], or -2 when either node is missing or not yet embedded (so
// "not comparable" is distinguishable from "genuinely orthogonal" 0.0).
fn route_similarity(method: String, path: String, body: String) -> String {
let a: String = query_param(path, "a")
let b: String = query_param(path, "b")
if str_eq(a, "") { return err_json("missing a") }
if str_eq(b, "") { return err_json("missing b") }
let sim: Float = engram_cosine_sim(a, b)
"{\"a\":\"" + a + "\",\"b\":\"" + b + "\",\"cosine\":" + float_to_str(sim) + "}"
fn route_health(method: String, path: String, body: String) -> String {
"{\"status\":\"ok\",\"engine\":\"engram-runtime-native\"}"
}
// Auth
@@ -699,32 +232,15 @@ fn handle_request(method: String, path: String, body: String) -> String {
}
}
// ISE posting is auth-exempt (internal soul daemon, same host, no _auth key)
if str_eq(method, "POST") && str_eq(clean, "/api/neuron/state-events") {
return route_emit_ise(method, path, body)
}
// Auth (when ENGRAM_API_KEY is set)
if !check_auth_ok(method, body) {
return err_json("unauthorized")
}
// Knowledge capture (auth enforced above; the world-ingestor integrator
// and any headless session without MCP push knowledge through this)
if str_eq(method, "POST") && str_eq(clean, "/api/neuron/knowledge/capture") {
return route_capture_knowledge(method, path, body)
}
// Stats
if str_eq(method, "GET") && (str_eq(clean, "/api/stats") || str_eq(clean, "/stats")) {
return route_stats(method, path, body)
}
if str_eq(method, "GET") && (str_eq(clean, "/api/act-stats") || str_eq(clean, "/act-stats")) {
return route_act_stats(method, path, body)
}
if str_eq(method, "GET") && (str_eq(clean, "/api/text-health") || str_eq(clean, "/text-health")) {
return route_text_health(method, path, body)
}
// Nodes
if str_eq(method, "POST") && (str_eq(clean, "/api/nodes") || str_eq(clean, "/nodes")) {
@@ -747,13 +263,6 @@ fn handle_request(method: String, path: String, body: String) -> String {
if str_eq(method, "POST") && (str_eq(clean, "/api/edges") || str_eq(clean, "/edges")) {
return route_create_edge(method, path, body)
}
// Batch edge write one snapshot for the whole payload. Must be tested
// BEFORE nothing else claims it; the exact-match on "/api/edges" above
// does not catch "/api/edges/batch", so order is not load-bearing here,
// but keeping the two adjacent keeps them from drifting apart.
if str_eq(method, "POST") && (str_eq(clean, "/api/edges/batch") || str_eq(clean, "/edges/batch")) {
return route_create_edges_batch(method, path, body)
}
if str_eq(method, "GET") && str_starts_with(clean, "/api/neighbors/") {
return route_neighbors(method, path, body)
}
@@ -784,75 +293,21 @@ fn handle_request(method: String, path: String, body: String) -> String {
if str_eq(method, "POST") && (str_eq(clean, "/api/load") || str_eq(clean, "/load")) {
return route_load(method, path, body)
}
if str_eq(method, "POST") && (str_eq(clean, "/api/load-merge") || str_eq(clean, "/load-merge")) {
return route_load_merge(method, path, body)
}
// Sync soul daemon periodic pull of non-ISE knowledge into in-process graph
if str_eq(method, "GET") && str_eq(clean, "/api/sync") {
return route_sync(method, path, body)
}
// Embedding backfill pumped by the soul heartbeat (2026-07-25)
if str_eq(clean, "/api/embed-backfill") {
return route_embed_backfill(method, path, body)
}
// Semantic similarity probe (2026-08-01)
if str_eq(method, "GET") && str_starts_with(clean, "/api/similarity") {
return route_similarity(method, path, body)
}
"{\"error\":\"not found\",\"path\":\"" + clean + "\"}"
}
// Entry
let bind_raw: String = env("ENGRAM_BIND")
let bind_str: String = if str_eq(bind_raw, "") { ":8742" } else { bind_raw }
let bind_str: String = env("ENGRAM_BIND")
if str_eq(bind_str, "") { let bind_str = ":8742" }
let port: Int = parse_port(bind_str)
// On startup, try to load any existing snapshot (best effort).
// §18.2: resolve the data dir safely unset ENGRAM_DATA_DIR $HOME/.neuron/engram,
// never /tmp; fail loud if HOME is unresolvable (engram_resolve_data_dir exits).
let data_dir: String = engram_resolve_data_dir()
let data_dir: String = env("ENGRAM_DATA_DIR")
if str_eq(data_dir, "") { let data_dir = "/tmp/engram" }
let snapshot_path: String = data_dir + "/snapshot.json"
// ENGRAM_STORE (tiered paged store engram-tiered-storage-engine.md). When set,
// the durable owner is the paged store (neuron.egm + neuron.wal): engram_store_boot
// imports snapshot.json ONCE into a fresh neuron.egm, else replays the WAL and loads
// the store resident snapshot.json is never read again as the ongoing store. This
// closes the "restart reverted to a 17h-old snapshot" data-loss window. Flag-off
// (default): byte-for-byte the historical snapshot + optional-WAL boot below.
if store_on() {
engram_store_boot(data_dir)
println("[engram] ENGRAM_STORE enabled — tiered paged store is the durable owner")
} else {
engram_load(snapshot_path)
// WAL replay (design doc §6). Gated: default OFF is byte-identical to legacy
// snapshot-only boot. When ON, the snapshot above is the compaction BASE and
// the WAL carries every mutation since; replay reconstructs state to the last
// CRC-valid record, then opens the WAL for appending.
if wal_on() {
let replayed: Int = engram_wal_boot(data_dir)
println("[engram] WAL enabled — replayed " + int_to_str(replayed) + " records")
}
// 2026-07-21 self-review boot guard: if the snapshot file has content but the
// load produced 0 nodes, something is wrong (corrupt file / parse failure).
// Preserve the evidence and warn loudly and since read routes no longer write
// the canonical path, a bad boot can no longer clobber the good snapshot.
let boot_snap: String = fs_read(snapshot_path)
if !str_eq(boot_snap, "") {
if engram_node_count() == 0 {
println("[engram] WARNING: snapshot.json is non-empty but load produced 0 nodes — preserving copy at snapshot.failed-load.json")
fs_write(data_dir + "/snapshot.failed-load.json", boot_snap)
} else {
// Good load: keep a boot-time backup of the snapshot as loaded.
fs_write(data_dir + "/snapshot.boot-backup.json", boot_snap)
}
}
}
engram_load(snapshot_path)
println("[engram] runtime-native graph engine")
println("[engram] data_dir=" + data_dir)
-149
View File
@@ -1,149 +0,0 @@
/* bench_discrimination.c — M9 REFINEMENT bench: measures whether mean-centering
* the anisotropic nomic-embed-text space sharpens the §5 geometry operators on
* REAL data. Read-only over a COPY of the live store (never the live file).
*
* usage: bench_discrimination [store.egm]
* (or set ENGRAM_BENCH_STORE). If no store is given/openable it prints
* SKIP and exits 0 so it is safe in CI without live data.
*
* It picks two semantically distinct cohorts by keyword (domain A vs domain B),
* computes the global mean over the embed-eligible set (via engram_geo_mean_build
* the same offset the descriptor uses), then reports BEFORE (raw unit space)
* vs AFTER (mean-centered space):
* - cross-centroid cosine (lower = better separated)
* - cross-centroid Euclid dist (translation-invariant: a control)
* - intra-cohesion per domain (member cos to own centroid)
* - overlap operator (cross_cos / sqrt(intraA*intraB): ~1 = domains
* indistinguishable, ~0 = cleanly separated)
* - angular separation ratio z (centroid angle / summed angular spread)
* - mean pairwise cosine sample (the anisotropy headline; ~0.55 raw -> ~0 ctr)
*
* Pure C11; links engram_store.c + engram_geometry.c; -lm.
*/
#include "engram_store.h"
#include "engram_geometry.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <math.h>
#define CAP_DOMAIN 400
#define CAP_SAMPLE 800
typedef struct { float** v; int n, cap, dim; } VecSet;
static void vs_init(VecSet* s){ s->v=NULL; s->n=0; s->cap=0; s->dim=0; }
static void vs_push(VecSet* s, const float* e, int dim, int cap){
if(s->n>=cap) return;
if(s->dim==0) s->dim=dim;
if(s->n==s->cap){ int nc=s->cap?s->cap*2:64; s->v=realloc(s->v,(size_t)nc*sizeof*s->v); s->cap=nc; }
float* c=malloc((size_t)dim*sizeof(float));
double nn=0; for(int d=0;d<dim;d++) nn+=(double)e[d]*e[d]; nn=sqrt(nn);
if(nn<1e-12){ free(c); return; }
for(int d=0;d<dim;d++) c[d]=(float)(e[d]/nn); /* L2-normalized copy */
s->v[s->n++]=c;
}
static void vs_free(VecSet* s){ for(int i=0;i<s->n;i++) free(s->v[i]); free(s->v); }
typedef struct { VecSet A, B, S; long idx; } Coh;
static int has(const char* h, const char* n){ return h && strcasestr(h,n)!=NULL; }
static void cb(const StoreNode* n, void* ctx){
Coh* c=ctx;
if(!(n->emb && n->emb_dim>0)) return;
/* every 5th embedded node -> isotropy sample */
if((c->idx++ % 5)==0) vs_push(&c->S, n->emb, n->emb_dim, CAP_SAMPLE);
const char* t=n->content; const char* g=n->tags;
int A = has(t,"quantiz")||has(g,"quantiz")||has(t,"lorablation")||has(t,"70B")||has(t,"LoRA merge");
int B = has(t,"kubernetes")||has(t,"terraform")||has(t,"argo")||has(g,"infrastructure")||has(t,"vault")||has(t,"cloudflare");
if(A && !B) vs_push(&c->A, n->emb, n->emb_dim, CAP_DOMAIN);
else if(B && !A) vs_push(&c->B, n->emb, n->emb_dim, CAP_DOMAIN);
}
/* mean of a VecSet into out (dim doubles). */
static void mean_of(const VecSet* s, const float* gm, double* out){
int dim=s->dim; for(int d=0;d<dim;d++) out[d]=0;
for(int i=0;i<s->n;i++) for(int d=0;d<dim;d++) out[d]+=(double)s->v[i][d]-(gm?gm[d]:0.0);
if(s->n) for(int d=0;d<dim;d++) out[d]/=s->n;
}
static double dnorm(const double* a, int dim){ double s=0; for(int d=0;d<dim;d++) s+=a[d]*a[d]; return sqrt(s); }
static double dcos(const double* a, const double* b, int dim){
double na=dnorm(a,dim), nb=dnorm(b,dim); if(na<1e-12||nb<1e-12) return 0;
double s=0; for(int d=0;d<dim;d++) s+=a[d]*b[d]; double c=s/(na*nb);
if(c>1)c=1; if(c<-1)c=-1; return c;
}
static double deuclid(const double* a, const double* b, int dim){
double s=0; for(int d=0;d<dim;d++){ double x=a[d]-b[d]; s+=x*x; } return sqrt(s);
}
/* mean cosine of members (minus gm) to centroid c (already gm-subtracted). */
static double cohesion(const VecSet* s, const float* gm, const double* c){
int dim=s->dim; double nc=dnorm(c,dim); if(nc<1e-12||s->n==0) return 0;
double acc=0; for(int i=0;i<s->n;i++){
double dot=0, nv=0;
for(int d=0;d<dim;d++){ double v=(double)s->v[i][d]-(gm?gm[d]:0.0); dot+=v*c[d]; nv+=v*v; }
nv=sqrt(nv); if(nv<1e-12) continue; double cc=dot/(nv*nc);
if(cc>1)cc=1; if(cc<-1)cc=-1; acc+=cc;
}
return acc/s->n;
}
/* mean pairwise cosine over a sample (isotropy metric). */
static double mean_pairwise_cos(const VecSet* s, const float* gm){
int dim=s->dim; if(s->n<2) return 0; double acc=0; long np=0;
for(int i=0;i<s->n;i++) for(int j=i+1;j<s->n;j++){
double dot=0, na=0, nb=0;
for(int d=0;d<dim;d++){ double a=(double)s->v[i][d]-(gm?gm[d]:0.0), b=(double)s->v[j][d]-(gm?gm[d]:0.0);
dot+=a*b; na+=a*a; nb+=b*b; }
na=sqrt(na); nb=sqrt(nb); if(na<1e-12||nb<1e-12) continue;
double c=dot/(na*nb); if(c>1)c=1; if(c<-1)c=-1; acc+=c; np++;
}
return np? acc/np : 0;
}
static void report(const char* label, Coh* c, const float* gm){
int dim=c->A.dim; double* ca=malloc((size_t)dim*sizeof(double)); double* cb=malloc((size_t)dim*sizeof(double));
mean_of(&c->A, gm, ca); mean_of(&c->B, gm, cb);
double xcos=dcos(ca,cb,dim), xeuc=deuclid(ca,cb,dim);
double cohA=cohesion(&c->A,gm,ca), cohB=cohesion(&c->B,gm,cb);
double overlap = (cohA>0&&cohB>0)? xcos/sqrt(cohA*cohB) : xcos;
double theta = acos(xcos<-1?-1:(xcos>1?1:xcos));
double sig = acos(cohA<-1?-1:(cohA>1?1:cohA)) + acos(cohB<-1?-1:(cohB>1?1:cohB));
double z = (sig>1e-9)? theta/sig : 0;
double mpc = mean_pairwise_cos(&c->S, gm);
printf(" [%s]\n", label);
printf(" cross-centroid cosine = %+.4f (lower = better separated)\n", xcos);
printf(" cross-centroid Euclid = %.4f (translation-invariant control)\n", xeuc);
printf(" intra-cohesion A / B = %.4f / %.4f\n", cohA, cohB);
printf(" OVERLAP operator = %.4f (~1 = indistinguishable, ~0 = clean)\n", overlap);
printf(" angular separation z = %.3f (centroid-angle / summed spread; >1 = separated)\n", z);
printf(" mean pairwise cosine = %+.4f (isotropy: ~0.55 anisotropic -> ~0 isotropic)\n", mpc);
free(ca); free(cb);
}
int main(int argc, char** argv){
const char* path = (argc>1)? argv[1] : getenv("ENGRAM_BENCH_STORE");
if(!path){ printf("SKIP: no store path (arg or ENGRAM_BENCH_STORE)\n"); return 0; }
EngramPagedStore* st=store_open(path);
if(!st){ printf("SKIP: could not open %s\n", path); return 0; }
Coh c; vs_init(&c.A); vs_init(&c.B); vs_init(&c.S); c.idx=0;
store_scan_nodes(st, cb, &c);
printf("=== two-domain discrimination bench (real store copy) ===\n");
printf("domain A (quantization) n=%d ; domain B (infrastructure) n=%d ; sample n=%d ; dim=%d\n",
c.A.n, c.B.n, c.S.n, c.A.dim);
if(c.A.n<3 || c.B.n<3){ printf("SKIP: a cohort is too small to be meaningful\n");
vs_free(&c.A); vs_free(&c.B); vs_free(&c.S); store_close(st); return 0; }
GeoMeanCache* mc=engram_geo_mean_build(st);
const float* gm=engram_geo_mean_vec(mc);
printf("global-mean cache: dim=%d over %llu embedded nodes\n\n",
engram_geo_mean_dim(mc), (unsigned long long)engram_geo_mean_count(mc));
printf("BEFORE (raw anisotropic unit space):\n");
report("RAW", &c, NULL);
printf("\nAFTER (mean-centered isotropic space):\n");
report("CENTERED", &c, gm);
engram_geo_mean_free(mc);
vs_free(&c.A); vs_free(&c.B); vs_free(&c.S);
store_close(st);
return 0;
}
-22
View File
@@ -1,22 +0,0 @@
#!/usr/bin/env bash
# M4 demand-paging buffer-pool gate. Pure C (NOT elb/elc). Writes only under /tmp.
# Runs the suite twice: an -O2 correctness build and an ASan+UBSan build.
set -e
HERE="$(cd "$(dirname "$0")" && pwd)"
SRC="$HERE/../../lang/runtime/engram_store.c"
TST="$HERE/test_bufpool.c"
echo "== compiling (gcc -O2): test_bufpool.c engram_store.c =="
BIN="/tmp/test_bufpool.$$"
gcc -O2 -Wall -Wextra -std=c11 "$TST" "$SRC" -o "$BIN"
"$BIN"; rc=$?
rm -f "$BIN"; rm -rf /tmp/engram-bufpool-test-*
[ $rc -ne 0 ] && exit $rc
echo
echo "== ASan+UBSan build (memory-error + UB checks; LSan unavailable on macOS) =="
ABIN="/tmp/test_bufpool_asan.$$"
gcc -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer -std=c11 "$TST" "$SRC" -o "$ABIN"
ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1 "$ABIN"; rc=$?
rm -f "$ABIN"; rm -rf /tmp/engram-bufpool-test-*
exit $rc
-22
View File
@@ -1,22 +0,0 @@
#!/usr/bin/env bash
# M5 online-compaction + background-checkpointer gate. Pure C (NOT elb/elc).
# Writes only under /tmp. Runs an -O2 correctness build then an ASan+UBSan build.
set -e
HERE="$(cd "$(dirname "$0")" && pwd)"
SRC="$HERE/../../lang/runtime/engram_store.c"
TST="$HERE/test_compaction.c"
echo "== compiling (gcc -O2): test_compaction.c engram_store.c =="
BIN="/tmp/test_compaction.$$"
gcc -O2 -Wall -Wextra -std=c11 "$TST" "$SRC" -o "$BIN"
"$BIN"; rc=$?
rm -f "$BIN"; rm -rf /tmp/engram-compact-test-*
[ $rc -ne 0 ] && exit $rc
echo
echo "== ASan+UBSan build (memory-error + UB checks; LSan unavailable on macOS) =="
ABIN="/tmp/test_compaction_asan.$$"
gcc -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer -std=c11 "$TST" "$SRC" -o "$ABIN"
ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1 "$ABIN"; rc=$?
rm -f "$ABIN"; rm -rf /tmp/engram-compact-test-*
exit $rc
-30
View File
@@ -1,30 +0,0 @@
#!/bin/sh
# Build + RUN the M9 FOUNDATION geometry-descriptor tests. Pure C11 (gcc/cc),
# stdlib + libm only. Standalone module — NOT folded through elb/elc. Two passes:
# 1. PERF — optimised (-O2, no sanitizer): the functional gate.
# 2. SAFETY — ASan + UBSan on the same suite (memory-safety is size-independent).
set -e
HERE=$(cd "$(dirname "$0")" && pwd)
RT="$HERE/../../lang/runtime"
CC=${CC:-cc}
SRC="$HERE/test_geometry.c $RT/engram_geometry.c $RT/engram_store.c $RT/engram_vindex.c"
WARN="-std=c11 -Wall -Wextra"
TMP=$(mktemp -d)
echo "### PASS 1: PERF (optimised, un-sanitised) — functional gate"
$CC $WARN -O2 -I"$RT" $SRC -lm -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 -o "$TMP/safe"
ASAN_OPTIONS=${ASAN_OPTIONS:-detect_leaks=0} UBSAN_OPTIONS=halt_on_error=1 "$TMP/safe"
# PASS 3 (OPTIONAL): mean-centering discrimination bench on a COPY of a real
# store. Skips cleanly unless ENGRAM_BENCH_STORE points at a store .egm — never
# touches the live store. Read-only; not part of the pass/fail gate.
echo
echo "### PASS 3: DISCRIMINATION BENCH (optional; set ENGRAM_BENCH_STORE)"
BSRC="$HERE/bench_discrimination.c $RT/engram_geometry.c $RT/engram_store.c $RT/engram_vindex.c"
$CC $WARN -O2 -I"$RT" $BSRC -lm -o "$TMP/bench"
"$TMP/bench" "${ENGRAM_BENCH_STORE:-}"
-86
View File
@@ -1,86 +0,0 @@
#!/usr/bin/env bash
# M-INTEROCEPTION P0 gate: engram_scan_nodes_emb_json read-only builtin.
# Throwaway HOME + /tmp only. Never touches ~/.neuron or :8742.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
GEO="$HERE/../../lang/runtime/engram_geometry.c"
VIDX="$HERE/../../lang/runtime/engram_vindex.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-p0-XXXXXX)"
export HOME="$WORK/home"; mkdir -p "$HOME"
unset ENGRAM_STORE
fail=0
echo "== compile (plain) =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_interoception_p0_emb.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p0" 2>"$WORK/cc.log" || { echo "COMPILE FAILED"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; }
D="$WORK/d"; mkdir -p "$D"
"$WORK/p0" "$D" || { echo "FAIL: run"; fail=1; }
echo
echo "== assertions =="
python3 - "$D" <<'PY'
import json, sys, os
d = sys.argv[1]
def load(n):
with open(os.path.join(d,n)) as f: return json.load(f)
rc = 0
def check(c,m):
global rc
print((" PASS: " if c else " FAIL: ")+m)
if not c: rc=1
alln = load("emb_all.json")
check(len(alln)==3, f"emb dump returns all 3 nodes (got {len(alln)})")
# salience-sorted: high, mid, low
labels=[n["label"] for n in alln]
check(labels==["emb-high","emb-mid","noemb-low"], f"salience-sorted order {labels}")
for n in alln:
L=len(n["emb"])
check(L==n["emb_dim"], f"{n['label']}: len(emb)={L} == emb_dim={n['emb_dim']}")
check(alln[0]["emb_dim"]==16 and alln[1]["emb_dim"]==16, "embedded nodes report dim 16")
check(alln[2]["emb_dim"]==0 and alln[2]["emb"]==[], "un-embedded node -> emb_dim 0, emb []")
# first emb value round-trips ~0.10
check(abs(alln[0]["emb"][0]-0.10)<1e-3, f"emb[0] round-trips (~0.10, got {alln[0]['emb'][0]})")
pg0=load("emb_pg0.json"); pg1=load("emb_pg1.json")
check(len(pg0)==1 and len(pg1)==1, "pagination: one node per page")
check(pg0[0]["id"]=="n-high" and pg1[0]["id"]=="n-mid", f"pages disjoint & ordered ({pg0[0]['id']},{pg1[0]['id']})")
plain=load("plain.json")
check(len(plain)==3, "existing scan_nodes_json still returns 3")
check(all("emb" not in n for n in plain), "existing scan_nodes_json carries NO emb (behavior-neutral)")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== latency (one 256-page over the 3-node copy) =="
python3 - "$D" <<'PY'
import os
# timing was measured inside C not here; report emb payload size as a proxy
sz=os.path.getsize(os.path.join(os.sys.argv[1] if False else __import__('sys').argv[1],"emb_all.json"))
print(f" emb_all.json payload = {sz} bytes for 3 nodes")
PY
echo
echo "== ASan+UBSan =="
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_interoception_p0_emb.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p0.san" 2>"$WORK/san_cc.log" || { echo "SAN COMPILE FAILED"; tail -20 "$WORK/san_cc.log"; fail=1; }
if [ -x "$WORK/p0.san" ]; then
export ASAN_OPTIONS=detect_leaks=0
DS="$WORK/ds"; mkdir -p "$DS"
"$WORK/p0.san" "$DS" >/dev/null 2>"$WORK/san_run.log"
if grep -qiE 'runtime error|AddressSanitizer|Sanitizer|ERROR: ' "$WORK/san_run.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san_run.log" | head; fail=1
else echo " ok: ASan+UBSan clean"; fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "====== P0 EMB-ENDPOINT GATE: PASS ======"; else echo "====== P0 EMB-ENDPOINT GATE: FAIL ======"; fi
rm -rf "$WORK"
exit $fail
-147
View File
@@ -1,147 +0,0 @@
#!/usr/bin/env bash
# M-INTEROCEPTION P1 gate: two-threshold consolidation (ENGRAM_CONSOLIDATION).
# Throwaway HOME + /tmp only. Never touches ~/.neuron or :8742.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
GEO="$HERE/../../lang/runtime/engram_geometry.c"
VIDX="$HERE/../../lang/runtime/engram_vindex.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-p1-XXXXXX)"
export HOME="$WORK/home"; mkdir -p "$HOME"
unset ENGRAM_STORE ENGRAM_CONSOLIDATION ENGRAM_CONSOL_CONN_MIN ENGRAM_CONSOL_PERM_MIN ENGRAM_CONSOL_WM_TOPK
fail=0
echo "== compile =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_interoception_p1_consol.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p1" 2>"$WORK/cc.log" || { echo "COMPILE FAILED"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; }
echo
echo "== (a) HEADLINE: hebb accrual curve over N co-activations (flag OFF, pure trunk) =="
D="$WORK/a"; mkdir -p "$D"
( unset ENGRAM_CONSOLIDATION; "$WORK/p1" accrual "$D" ) >"$WORK/accrual.txt" 2>&1 || { echo "FAIL accrual run"; fail=1; }
python3 - "$WORK/accrual.txt" <<'PY'
import json,sys,re
rows=[]
for line in open(sys.argv[1]):
m=re.match(r'SAMPLE (\d+) (\{.*\})',line.strip())
if not m: continue
n=int(m.group(1)); j=json.loads(m.group(2))
hm=j.get("hebb_max",0.0); hc=j.get("hebb_cand_max",0.0)
rows.append((n,hm,hc))
print(" N hebb_max 1-0.9999^N (predicted EWMA)")
rc=0
for n,hm,hc in rows:
pred=1-0.9999**n
print(f" {n:<7} {hm:<12.6g} {pred:.6g}")
# assertions: monotonic rise, starts near ETA, tracks EWMA prediction
first=rows[0]; last=rows[-1]
def check(c,m):
global rc; print((" PASS: " if c else " FAIL: ")+m);
if not c: rc=1
check(abs(first[1]-0.0001)<5e-5, f"first sample hebb ~= ETA 0.0001 (got {first[1]:.6g})")
check(all(rows[i][1] <= rows[i+1][1]+1e-9 for i in range(len(rows)-1)), "hebb_max is monotonically non-decreasing over N")
check(last[1] > first[1]*50, f"hebb accrues substantially by N={last[0]} (got {last[1]:.4g} vs {first[1]:.4g})")
# EWMA fit: measured should be within 25% of 1-0.9999^N at the mid samples
mid=[r for r in rows if 100<=r[0]<=2000]
ok=all(abs(hm-(1-0.9999**n))/(1-0.9999**n) < 0.25 for n,hm,hc in mid)
check(ok, "measured curve tracks the 1-0.9999^N EWMA prediction within 25% (co-activation P~1)")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== (b) CONNECTION threshold: strong ISE wires to wm_top, weak ISE wires nothing (flag ON) =="
D="$WORK/b"; mkdir -p "$D"
( export ENGRAM_CONSOLIDATION=1; "$WORK/p1" connect "$D" ) >"$WORK/connect.txt" 2>&1 || { echo "FAIL connect run"; fail=1; }
cat "$WORK/connect.txt" | sed 's/^/ /'
python3 - "$WORK/connect.txt" "$D/connect.json" <<'PY'
import json,sys,re
txt=open(sys.argv[1]).read()
g=json.load(open(sys.argv[2]))
def field(k):
m=re.search(rf'{k} (\S+)',txt); return m.group(1) if m else None
sid=field("ISE_STRONG_ID"); wid=field("ISE_WEAK_ID")
m=re.search(r'EDGES before=(\d+) after_strong=(\d+) after_weak=(\d+)',txt)
before,aftS,aftW=int(m.group(1)),int(m.group(2)),int(m.group(3))
rc=0
def check(c,mm):
global rc; print((" PASS: " if c else " FAIL: ")+mm)
if not c: rc=1
strong_edges=[e for e in g["edges"] if e["from_id"]==sid and e["relation"]=="hebbian-associate"]
weak_edges=[e for e in g["edges"] if e["from_id"]==wid]
check(aftS>before, f"strong ISE formed connection edges ({before} -> {aftS})")
check(aftW==aftS, f"weak ISE formed NO edges ({aftS} -> {aftW})")
check(len(strong_edges)>=1, f"strong ISE has {len(strong_edges)} hebbian-associate edge(s) to wm_top")
check(all('consolidated-from-ISE' in (e.get('metadata') or '') for e in strong_edges),
"connection edges are provenance-tagged consolidated-from-ISE (reversible)")
check(len(weak_edges)==0, "weak ISE (below connection bar) has zero outgoing edges")
# targets must be the WM-top nodes (hebb-a / hebb-b), not distractors
tgt_labels=set()
byid={n["id"]:n for n in g["nodes"]}
for e in strong_edges:
t=byid.get(e["to_id"]);
if t: tgt_labels.add(t.get("label"))
print(f" connection targets: {sorted(tgt_labels)}")
check(tgt_labels.issubset({"hebb-a","hebb-b"}) and len(tgt_labels)>=1,
f"connections point at the wm_top nodes {sorted(tgt_labels)}")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== (c) PERMANENCE threshold: promoted node survives 48h prune, ephemeral is swept (flag ON) =="
D="$WORK/c"; mkdir -p "$D"
( export ENGRAM_CONSOLIDATION=1 ENGRAM_CONSOL_PERM_MIN=-1000; "$WORK/p1" perm "$D" ) >"$WORK/perm.txt" 2>&1 || { echo "FAIL perm run"; fail=1; }
cat "$WORK/perm.txt" | sed 's/^/ /'
python3 - "$WORK/perm.txt" <<'PY'
import sys,re,json
txt=open(sys.argv[1]).read()
rc=0
def check(c,m):
global rc; print((" PASS: " if c else " FAIL: ")+m)
if not c: rc=1
prom=int(re.search(r'PROMOTED (\d+)',txt).group(1))
m=re.search(r'NODES before=(\d+) after=(\d+) removed=(\d+)',txt)
before,after,removed=int(m.group(1)),int(m.group(2)),int(m.group(3))
dur=re.search(r'DURABLE_NODE (\{.*\})',txt).group(1)
eph=re.search(r'EPHEMERAL_NODE (\{.*\})',txt).group(1)
durj=json.loads(dur); ephj=json.loads(eph)
check(prom==1, "engram_consolidate_permanence promoted the node (returned 1)")
check(before==2 and after==1 and removed==1, f"exactly one node pruned ({before}->{after}, removed={removed})")
check(durj.get("id")=="ise-durable", "durable node SURVIVED the 48h telemetry prune")
check('consolidated-from-ISE' in (durj.get("metadata") or ''), "durable node carries reversible provenance marker")
check(ephj=={} or not ephj.get("id"), "ephemeral (non-permanent) ISE was swept")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== (d) OFF path byte-identical: ISE creation forms no edges, permanence is a no-op =="
D="$WORK/d"; mkdir -p "$D"
( unset ENGRAM_CONSOLIDATION; "$WORK/p1" offcheck "$D" ) >"$WORK/off.txt" 2>&1
rcoff=$?
cat "$WORK/off.txt" | sed 's/^/ /'
[ $rcoff -eq 0 ] && echo " PASS: flag OFF — ISE creation added 0 edges and permanence returned 0" \
|| { echo " FAIL: OFF path changed behavior"; fail=1; }
echo
echo "== ASan+UBSan (connect + perm + accrual-short) =="
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_interoception_p1_consol.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p1.san" 2>"$WORK/san_cc.log" || { echo "SAN COMPILE FAILED"; tail -25 "$WORK/san_cc.log"; fail=1; }
if [ -x "$WORK/p1.san" ]; then
export ASAN_OPTIONS=detect_leaks=0
DS="$WORK/san"; mkdir -p "$DS"
( export ENGRAM_CONSOLIDATION=1 ENGRAM_CONSOL_PERM_MIN=-1000; "$WORK/p1.san" connect "$DS" ) >/dev/null 2>"$WORK/san_run.log"
( export ENGRAM_CONSOLIDATION=1 ENGRAM_CONSOL_PERM_MIN=-1000; "$WORK/p1.san" perm "$DS" ) >/dev/null 2>>"$WORK/san_run.log"
if grep -qiE 'runtime error|AddressSanitizer|Sanitizer|ERROR: ' "$WORK/san_run.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san_run.log" | head; fail=1
else echo " ok: ASan+UBSan clean"; fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "====== P1 CONSOLIDATION GATE: PASS ======"; else echo "====== P1 CONSOLIDATION GATE: FAIL ======"; fi
rm -rf "$WORK"
exit $fail
-96
View File
@@ -1,96 +0,0 @@
#!/usr/bin/env bash
# M-INTEROCEPTION P2 gate: chronoception (ENGRAM_CHRONOCEPTION).
# Throwaway HOME + /tmp only. TC defaults to 3600s; we pin it for the math.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
GEO="$HERE/../../lang/runtime/engram_geometry.c"
VIDX="$HERE/../../lang/runtime/engram_vindex.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-p2-XXXXXX)"
export HOME="$WORK/home"; mkdir -p "$HOME"
export ENGRAM_CHRONO_TC=3600 # pin cooling time-constant for the math
unset ENGRAM_STORE
fail=0
echo "== compile =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_interoception_p2_chrono.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p2" 2>"$WORK/cc.log" || { echo "COMPILE FAILED"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; }
sum_wm(){ python3 -c "import json,sys; g=json.load(open('$1')); print(sum(n.get('working_memory_weight',0) for n in g['nodes']))"; }
echo
echo "== (a) cooling scales with dt (flag ON) =="
for DT in 600000 1800000 3600000 7200000; do # 600s,1800s,3600s,7200s at TC=3600
D="$WORK/dt$DT"; mkdir -p "$D"
( export ENGRAM_CHRONOCEPTION=1; "$WORK/p2" once "$D" "$DT" ) >"$D/out.txt" 2>&1
MAG=$(grep MAGNITUDE "$D/out.txt" | awk '{print $2}')
WM=$(sum_wm "$D/field.json")
PRED=$(python3 -c "import math; print(round(1-math.exp(-$DT/1000/3600),6))")
echo " dt=${DT}ms magnitude=$MAG predicted 1-exp(-dt/TC)=$PRED field_wm_sum=$WM"
python3 -c "import sys; m=float('$MAG'); p=float('$PRED'); sys.exit(0 if abs(m-p)<1e-4 else 1)" \
&& echo " PASS: magnitude matches exp cooling" || { echo " FAIL"; fail=1; }
done
echo
echo "== (b) SCALE-INVARIANCE: age(dt) once == age(dt/N) N times (field within float tol) =="
DT=3600000
for N in 2 10 100; do
DA="$WORK/inv_once_$N"; DB="$WORK/inv_split_$N"; mkdir -p "$DA" "$DB"
( export ENGRAM_CHRONOCEPTION=1; "$WORK/p2" once "$DA" "$DT" ) >/dev/null 2>&1
( export ENGRAM_CHRONOCEPTION=1; "$WORK/p2" split "$DB" "$DT" "$N" ) >/dev/null 2>&1
WA=$(sum_wm "$DA/field.json"); WB=$(sum_wm "$DB/field.json")
echo " N=$N once_wm=$WA split_wm=$WB |delta|=$(python3 -c "print(abs($WA-$WB))")"
python3 -c "import sys; sys.exit(0 if abs($WA-$WB)<1e-9 else 1)" \
&& echo " PASS: scale-invariant within 1e-9" || { echo " FAIL: not scale-invariant"; fail=1; }
done
echo
echo "== (c) REBOOT catch-up: one-shot cooling from persisted last-tick, reports MAGNITUDE not seconds =="
D="$WORK/catch"; mkdir -p "$D"
GAP=3600000 # 1h unconscious
( export ENGRAM_CHRONOCEPTION=1 ENGRAM_DATA_DIR="$D"; "$WORK/p2" catchup "$D" "$GAP" ) >"$D/out.txt" 2>&1
CMAG=$(grep CATCHUP_MAGNITUDE "$D/out.txt" | awk '{print $2}')
CWM=$(sum_wm "$D/field.json")
PRED=$(python3 -c "import math; print(round(1-math.exp(-$GAP/1000/3600),4))")
echo " gap=${GAP}ms catchup_magnitude=$CMAG predicted=$PRED field_wm_sum=$CWM (was 0.6)"
python3 -c "import sys; sys.exit(0 if abs(float('$CMAG')-float('$PRED'))<1e-2 else 1)" \
&& echo " PASS: one-shot catch-up cooled by the elapsed gap, surfaced as a magnitude" \
|| { echo " FAIL"; fail=1; }
# honesty rail: magnitude is bounded [0,1), NOT an elapsed-seconds number
python3 -c "import sys; m=float('$CMAG'); sys.exit(0 if 0<=m<1 else 1)" \
&& echo " PASS: magnitude is a bounded drift signal in [0,1), never elapsed seconds" \
|| { echo " FAIL: magnitude out of [0,1)"; fail=1; }
echo
echo "== (d) OFF path: flag unset -> age & catchup return 0, field untouched =="
D="$WORK/off"; mkdir -p "$D"
( unset ENGRAM_CHRONOCEPTION; export ENGRAM_DATA_DIR="$D"; "$WORK/p2" offcheck "$D" 3600000 ) >"$D/out.txt" 2>&1
cat "$D/out.txt" | sed 's/^/ /'
OFFWM=$(sum_wm "$D/field.json")
# loaded field wm sum = (1.0+0.8+0.6)*0.5 halving = 1.2 ; must be UNCHANGED
echo " field_wm_sum=$OFFWM (expected 1.2, unchanged)"
python3 -c "import sys; sys.exit(0 if abs($OFFWM-1.2)<1e-9 else 1)" \
&& echo " PASS: OFF path leaves the field byte-identical (no aging)" \
|| { echo " FAIL: OFF path modified the field"; fail=1; }
echo
echo "== ASan+UBSan =="
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_interoception_p2_chrono.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p2.san" 2>"$WORK/san_cc.log" || { echo "SAN COMPILE FAILED"; tail -25 "$WORK/san_cc.log"; fail=1; }
if [ -x "$WORK/p2.san" ]; then
export ASAN_OPTIONS=detect_leaks=0
DS="$WORK/san"; mkdir -p "$DS"
( export ENGRAM_CHRONOCEPTION=1 ENGRAM_DATA_DIR="$DS"; "$WORK/p2.san" once "$DS" 3600000 ) >/dev/null 2>"$WORK/san.log"
( export ENGRAM_CHRONOCEPTION=1 ENGRAM_DATA_DIR="$DS"; "$WORK/p2.san" catchup "$DS" 3600000 ) >/dev/null 2>>"$WORK/san.log"
if grep -qiE 'runtime error|AddressSanitizer|Sanitizer|ERROR: ' "$WORK/san.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san.log" | head; fail=1
else echo " ok: ASan+UBSan clean"; fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "====== P2 CHRONOCEPTION GATE: PASS ======"; else echo "====== P2 CHRONOCEPTION GATE: FAIL ======"; fi
rm -rf "$WORK"
exit $fail
-68
View File
@@ -1,68 +0,0 @@
#!/usr/bin/env bash
# M-INTEROCEPTION P3 gate: drift-sensor primitive engram_geo_displacement.
# Read-only pure primitive; no store, no flag. Throwaway /tmp only.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
GEO="$HERE/../../lang/runtime/engram_geometry.c"
VIDX="$HERE/../../lang/runtime/engram_vindex.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-p3-XXXXXX)"
export HOME="$WORK/home"; mkdir -p "$HOME"
fail=0
echo "== compile =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_interoception_p3_drift.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p3" 2>"$WORK/cc.log" || { echo "COMPILE FAILED"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; }
"$WORK/p3" > "$WORK/out.txt" 2>&1 || { echo "FAIL run"; cat "$WORK/out.txt"; fail=1; }
cat "$WORK/out.txt" | sed 's/^/ /'
echo
echo "== assertions =="
python3 - "$WORK/out.txt" <<'PY'
import sys,re
rows={}
for line in open(sys.argv[1]):
m=re.match(r'(\w+) (.*)',line.strip())
if not m: continue
tag=m.group(1); kv=dict(re.findall(r'(\w+)=([-\d.]+)',m.group(2)))
rows[tag]={k:float(v) for k,v in kv.items()}
rc=0
def check(c,msg):
global rc; print((" PASS: " if c else " FAIL: ")+msg)
if not c: rc=1
g=rows["GROWTH"]; c=rows["CORRUPTION"]; i=rows["IDENTITY"]
check(g["core_disp"]<0.05, f"GROWTH: core displacement ~0 (core fixed) = {g['core_disp']}")
check(g["periph_disp"]>0.30, f"GROWTH: periphery extended = {g['periph_disp']}")
check(g["centroid_sep"]<1e-6, f"GROWTH: centroid unmoved = {g['centroid_sep']}")
check(abs(g["radius_delta"]-0.4)<1e-4, f"GROWTH: radius grew by ~0.4 = {g['radius_delta']}")
check(c["core_disp"]>0.40, f"CORRUPTION: core displaced strongly = {c['core_disp']}")
check(c["periph_disp"]<0.05, f"CORRUPTION: periphery fixed = {c['periph_disp']}")
check(c["centroid_sep"]>0.1, f"CORRUPTION: centroid moved = {c['centroid_sep']}")
check(c["core_disp"] > 8*g["core_disp"]+0.3,
f"SENSOR DISCRIMINATES: corruption core_disp ({c['core_disp']}) >> growth core_disp ({g['core_disp']})")
check(i["core_disp"]==0 and i["periph_disp"]==0 and i["centroid_sep"]<1e-6,
"IDENTITY: A vs A -> zero drift")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== ASan+UBSan =="
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_interoception_p3_drift.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p3.san" 2>"$WORK/san_cc.log" || { echo "SAN COMPILE FAILED"; tail -25 "$WORK/san_cc.log"; fail=1; }
if [ -x "$WORK/p3.san" ]; then
export ASAN_OPTIONS=detect_leaks=0
"$WORK/p3.san" >/dev/null 2>"$WORK/san.log"
if grep -qiE 'runtime error|AddressSanitizer|Sanitizer|ERROR: ' "$WORK/san.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san.log" | head; fail=1
else echo " ok: ASan+UBSan clean"; fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "====== P3 DRIFT-SENSOR GATE: PASS ======"; else echo "====== P3 DRIFT-SENSOR GATE: FAIL ======"; fi
rm -rf "$WORK"
exit $fail
-69
View File
@@ -1,69 +0,0 @@
#!/usr/bin/env bash
# M-INTEROCEPTION P4 gate: afferent input counters in act-stats (additive).
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
GEO="$HERE/../../lang/runtime/engram_geometry.c"
VIDX="$HERE/../../lang/runtime/engram_vindex.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-p4-XXXXXX)"
export HOME="$WORK/home"; mkdir -p "$HOME"
unset ENGRAM_STORE
fail=0
echo "== compile =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_interoception_p4_afferent.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p4" 2>"$WORK/cc.log" || { echo "COMPILE FAILED"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; }
"$WORK/p4" > "$WORK/out.txt" 2>&1 || { echo "FAIL run"; cat "$WORK/out.txt"; fail=1; }
grep -oE 'aff_[a-z_]+":[0-9]+' "$WORK/out.txt" | sed 's/^/ /' | head -30
echo
echo "== assertions =="
python3 - "$WORK/out.txt" <<'PY'
import sys,re,json
S={}
for line in open(sys.argv[1]):
m=re.match(r'(STATS\d) (\{.*\})',line.strip())
if m: S[m.group(1)]=json.loads(m.group(2))
rc=0
def check(c,msg):
global rc; print((" PASS: " if c else " FAIL: ")+msg)
if not c: rc=1
s0,s1,s2=S["STATS0"],S["STATS1"],S["STATS2"]
# after creation, before any query
check(s0["aff_node_creates"]==5, f"node_creates==5 (got {s0['aff_node_creates']})")
check(s0["aff_ise_ingests"]==2, f"ise_ingests==2 (got {s0['aff_ise_ingests']})")
check(s0["aff_edge_creates"]==2, f"edge_creates==2 (got {s0['aff_edge_creates']})")
check(s0["aff_queries"]==0 and s0["aff_activations"]==0, "queries/activations start at 0")
# after 4 queries
check(s1["aff_queries"]==4, f"queries==4 (got {s1['aff_queries']})")
check(s1["aff_activations"]==4, f"activations==4 (got {s1['aff_activations']})")
check(s1["aff_node_creates"]==5 and s1["aff_ise_ingests"]==2 and s1["aff_edge_creates"]==2,
"create counters unchanged by queries")
# after 3 more queries — monotonic
check(s2["aff_queries"]==7, f"queries==7 monotonic (got {s2['aff_queries']})")
check(s2["aff_activations"]==7, f"activations==7 monotonic (got {s2['aff_activations']})")
check(s2["aff_queries"]>s1["aff_queries"]>s0["aff_queries"], "queries strictly monotonic across readings")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== ASan+UBSan =="
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_interoception_p4_afferent.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p4.san" 2>"$WORK/san_cc.log" || { echo "SAN COMPILE FAILED"; tail -25 "$WORK/san_cc.log"; fail=1; }
if [ -x "$WORK/p4.san" ]; then
export ASAN_OPTIONS=detect_leaks=0
"$WORK/p4.san" >/dev/null 2>"$WORK/san.log"
if grep -qiE 'runtime error|AddressSanitizer|Sanitizer|ERROR: ' "$WORK/san.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san.log" | head; fail=1
else echo " ok: ASan+UBSan clean"; fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "====== P4 AFFERENT-COUNTERS GATE: PASS ======"; else echo "====== P4 AFFERENT-COUNTERS GATE: FAIL ======"; fi
rm -rf "$WORK"
exit $fail
-74
View File
@@ -1,74 +0,0 @@
#!/usr/bin/env bash
# M-INTEROCEPTION P5 gate: dream-recall builtin engram_dreams_json (honesty rail).
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
GEO="$HERE/../../lang/runtime/engram_geometry.c"
VIDX="$HERE/../../lang/runtime/engram_vindex.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-p5-XXXXXX)"
export HOME="$WORK/home"; mkdir -p "$HOME"
unset ENGRAM_STORE
fail=0
echo "== compile =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_interoception_p5_dreams.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p5" 2>"$WORK/cc.log" || { echo "COMPILE FAILED"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; }
D="$WORK/d"; mkdir -p "$D"
"$WORK/p5" "$D" > "$WORK/out.txt" 2>&1 || { echo "FAIL run"; cat "$WORK/out.txt"; fail=1; }
cat "$WORK/out.txt" | sed 's/^/ /'
echo
echo "== assertions =="
python3 - "$WORK/out.txt" <<'PY'
import sys,re,json
L={}
for line in open(sys.argv[1]):
line=line.strip()
m=re.match(r'(BEFORE|AFTER) (\[.*\])',line)
if m: L[m.group(1)]=json.loads(m.group(2)); continue
m=re.match(r'PRUNED (\d+)',line)
if m: L['PRUNED']=int(m.group(1)); continue
m=re.match(r'SINCE (\d+) (\[.*\])',line)
if m: L['SINCE']=json.loads(m.group(2))
rc=0
def check(c,msg):
global rc; print((" PASS: " if c else " FAIL: ")+msg)
if not c: rc=1
before_ids={d["id"] for d in L["BEFORE"]}
after_ids={d["id"] for d in L["AFTER"]}
since_ids={d["id"] for d in L["SINCE"]}
check(before_ids=={"cur_old","cur_mid","cur_recent"}, f"before prune: all 3 curiosity_scan, heartbeat excluded (got {sorted(before_ids)})")
check("hb_recent" not in before_ids, "heartbeat ISE never appears (not a dream)")
check(L["PRUNED"]==1, f"prune rotated out exactly the ancient ISE (pruned={L['PRUNED']})")
check(after_ids=={"cur_mid","cur_recent"}, f"after prune: rotated-out cur_old is ABSENT, not confabulated (got {sorted(after_ids)})")
check("cur_old" not in after_ids, "honesty rail: pruned dream is gone = 'I don't remember', never synthesized")
check(since_ids=={"cur_recent"}, f"since filter returns only events after the cutoff (got {sorted(since_ids)})")
# no fabrication: every returned id was one we seeded
seeded={"cur_old","cur_mid","cur_recent","hb_recent"}
allret=before_ids|after_ids|since_ids
check(allret<=seeded, f"no fabricated entries — every returned id was seeded ({sorted(allret)})")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== ASan+UBSan =="
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_interoception_p5_dreams.c" "$RT" "$ST" "$GEO" "$VIDX" \
-lcurl -lm -o "$WORK/p5.san" 2>"$WORK/san_cc.log" || { echo "SAN COMPILE FAILED"; tail -25 "$WORK/san_cc.log"; fail=1; }
if [ -x "$WORK/p5.san" ]; then
export ASAN_OPTIONS=detect_leaks=0
DS="$WORK/ds"; mkdir -p "$DS"
"$WORK/p5.san" "$DS" >/dev/null 2>"$WORK/san.log"
if grep -qiE 'runtime error|AddressSanitizer|Sanitizer|ERROR: ' "$WORK/san.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san.log" | head; fail=1
else echo " ok: ASan+UBSan clean"; fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "====== P5 DREAM-RECALL GATE: PASS ======"; else echo "====== P5 DREAM-RECALL GATE: FAIL ======"; fi
rm -rf "$WORK"
exit $fail
-158
View File
@@ -1,158 +0,0 @@
#!/usr/bin/env bash
# M3.5 PRE-FLIP GATE. Pure C harness (NOT elb/elc): links the real el_runtime.c
# native engram builtins + engram_store.c and proves activation-time field
# mutations (edge hebb, node activation_count, WM weight) persist through a
# checkpoint and survive a reboot from neuron.egm with snapshot.json DELETED.
# Writes ONLY under a throwaway /tmp dir with a throwaway HOME.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-m35-XXXXXX)"
BIN="$WORK/m35"
export HOME="$WORK/home"; mkdir -p "$HOME" # never touch real ~/.neuron
export ENGRAM_WAL_SYNC=always
unset ENGRAM_STORE
fail=0
echo "== compiling harness (gcc: el_runtime.c + engram_store.c + test_m35_hebb_persist.c) =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_m35_hebb_persist.c" "$RT" "$ST" -lcurl -o "$BIN" 2>"$WORK/cc.log"
if [ $? -ne 0 ]; then echo "COMPILE FAILED:"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; fi
echo
echo "== 0) flag-OFF: seed+activate+checkpoint must NOT touch the store =="
DOFF="$WORK/off"; mkdir -p "$DOFF"
( unset ENGRAM_STORE; "$BIN" offcheck "$DOFF" )
[ $? -ne 0 ] && { echo "FAIL: offcheck"; fail=1; }
[ -e "$DOFF/neuron.egm" ] && { echo "FAIL: neuron.egm created while flag OFF"; fail=1; } \
|| echo " ok: no neuron.egm created with flag OFF"
echo
echo "== 1) POSITIVE: ENGRAM_STORE=1 seed -> activate -> checkpoint(field-persist) -> close =="
DPOS="$WORK/pos"; mkdir -p "$DPOS"
ENGRAM_STORE=1 "$BIN" pos_seed "$DPOS" || { echo "FAIL: pos_seed"; fail=1; }
[ -e "$DPOS/neuron.egm" ] && echo " ok: neuron.egm created" || { echo "FAIL: neuron.egm missing"; fail=1; }
echo
echo "== 2) reboot from neuron.egm with snapshot.json DELETED (must never read JSON) =="
rm -f "$DPOS/snapshot.json"
ENGRAM_STORE=1 "$BIN" pos_reboot "$DPOS" || { echo "FAIL: pos_reboot"; fail=1; }
echo
echo "== 3) NEGATIVE CONTROL: seed -> activate -> close WITHOUT the field-persist checkpoint =="
DNEG="$WORK/neg"; mkdir -p "$DNEG"
ENGRAM_STORE=1 "$BIN" neg_seed "$DNEG" || { echo "FAIL: neg_seed"; fail=1; }
rm -f "$DNEG/snapshot.json"
ENGRAM_STORE=1 "$BIN" neg_reboot "$DNEG" || { echo "FAIL: neg_reboot"; fail=1; }
echo
echo "== 4) assertions (python over the JSON exports) =="
python3 - "$DPOS" "$DNEG" <<'PY'
import json, sys, os
WM_FLOOR = 0.05
HEBB_MIN = 1e-6
def load(d, name):
with open(os.path.join(d, name)) as f: return json.load(f)
def node_by_label(g, label):
for n in g["nodes"]:
if n.get("label") == label: return n
return None
def edge_between(g, a_id, b_id):
for e in g["edges"]:
if e.get("from_id") == a_id and e.get("to_id") == b_id:
return e
return None
rc = 0
def check(cond, msg):
global rc
if cond: print(f" PASS: {msg}")
else: print(f" FAIL: {msg}"); rc = 1
dpos, dneg = sys.argv[1], sys.argv[2]
pre = load(dpos, "pre_reboot.json")
rebt = load(dpos, "reboot.json")
pa, pb = node_by_label(pre, "hebb-a"), node_by_label(pre, "hebb-b")
ra = node_by_label(rebt, "hebb-a")
assert pa and pb and ra, "target nodes missing"
pe = edge_between(pre, pa["id"], pb["id"])
re = edge_between(rebt, pa["id"], pb["id"])
assert pe and re, "target edge missing"
pre_hebb = pe.get("hebb", 0.0)
rebt_hebb = re.get("hebb", 0.0)
pre_ac = pa.get("activation_count", 0)
rebt_ac = ra.get("activation_count", 0)
pre_wm = pa.get("working_memory_weight", 0.0)
rebt_wm = ra.get("working_memory_weight", 0.0)
print(f" edge hebb-a->hebb-b : pre={pre_hebb!r} reboot={rebt_hebb!r}")
print(f" node hebb-a act_cnt : pre={pre_ac!r} reboot={rebt_ac!r}")
print(f" node hebb-a wm : pre={pre_wm!r} reboot={rebt_wm!r} (halved+floored expected)")
# --- learning actually happened this run (else the test proves nothing) ---
check(pre_hebb > HEBB_MIN, f"activation raised edge hebb above 0 (pre={pre_hebb})")
check(pre_ac >= 1, f"activation reinforced node activation_count (pre={pre_ac})")
check(pre_wm > 0.0, f"activation promoted node to working memory (pre_wm={pre_wm})")
# --- the load-bearing survival assertions after a real delete-JSON reboot ---
check(abs(rebt_hebb - pre_hebb) < 1e-12,
f"edge hebb SURVIVED reboot unchanged ({rebt_hebb} == {pre_hebb})")
check(rebt_ac == pre_ac,
f"node activation_count SURVIVED reboot unchanged ({rebt_ac} == {pre_ac})")
# --- WM weight: must equal the JSON path's boot transform exactly (halve+floor) ---
expected_wm = pre_wm * 0.5
if expected_wm < WM_FLOOR: expected_wm = 0.0
check(abs(rebt_wm - expected_wm) < 1e-9,
f"node WM weight SURVIVED with the SAME boot transform as JSON path "
f"(reboot={rebt_wm} == halve+floor(pre)={expected_wm})")
check(expected_wm > 0.0,
f"WM survival is observable (halved weight stays above floor: {expected_wm} > {WM_FLOOR})")
# --- NEGATIVE CONTROL: without the field-persist step the learning is LOST ---
npre = load(dneg, "neg_pre.json")
nrebt = load(dneg, "neg_reboot.json")
na_pre = node_by_label(npre, "hebb-a")
na_rebt = node_by_label(nrebt, "hebb-a")
ne_pre = edge_between(npre, na_pre["id"], node_by_label(npre, "hebb-b")["id"])
ne_rebt = edge_between(nrebt, na_rebt["id"], node_by_label(nrebt, "hebb-b")["id"])
print(f" [neg] edge hebb : pre={ne_pre.get('hebb',0.0)!r} reboot={ne_rebt.get('hebb',0.0)!r}")
print(f" [neg] node act_cnt : pre={na_pre.get('activation_count',0)!r} reboot={na_rebt.get('activation_count',0)!r}")
check(ne_pre.get("hebb", 0.0) > HEBB_MIN,
f"[neg] activation DID raise hebb in RAM (pre={ne_pre.get('hebb',0.0)})")
check(ne_rebt.get("hebb", 0.0) == 0.0,
"[neg] WITHOUT checkpoint field-persist, edge hebb is LOST on reboot (==0) — fix is load-bearing")
check(na_rebt.get("activation_count", 0) == 0,
"[neg] WITHOUT checkpoint field-persist, activation_count is LOST on reboot (==0)")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== 5) ASan+UBSan build, exercise the full persist+reboot flow (leaks off — harness intentionally leaks el_strdup) =="
SANBIN="$WORK/m35.san"
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_m35_hebb_persist.c" "$RT" "$ST" -lcurl -o "$SANBIN" 2>"$WORK/san_cc.log"
if [ $? -ne 0 ]; then echo " SAN COMPILE FAILED:"; tail -20 "$WORK/san_cc.log"; fail=1; else
export ASAN_OPTIONS=detect_leaks=0
DSAN="$WORK/san"; mkdir -p "$DSAN"
ENGRAM_STORE=1 "$SANBIN" pos_seed "$DSAN" >/dev/null 2>"$WORK/san_run.log" && \
{ rm -f "$DSAN/snapshot.json"; ENGRAM_STORE=1 "$SANBIN" pos_reboot "$DSAN" >/dev/null 2>>"$WORK/san_run.log"; }
if grep -qiE 'runtime error|AddressSanitizer|UndefinedBehavior|ERROR: ' "$WORK/san_run.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san_run.log" | head; fail=1
else
echo " ok: ASan+UBSan clean across pos_seed/checkpoint/reboot (field-persist, boot laundering)"
fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "================ M3.5 HEBB-PERSIST GATE: PASS ================"; else echo "================ M3.5 HEBB-PERSIST GATE: FAIL ================"; fi
rm -rf "$WORK"
exit $fail
-126
View File
@@ -1,126 +0,0 @@
#!/usr/bin/env bash
# M3 JSON-parity gate. Pure C harness (NOT elb/elc): links the real el_runtime.c
# native engram builtins + engram_store.c and drives ENGRAM_STORE on vs off.
# Writes ONLY under a throwaway /tmp dir with a throwaway HOME + ENGRAM_DATA_DIR.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-m3-XXXXXX)"
DATA="$WORK/data"; mkdir -p "$DATA"
BIN="$WORK/m3"
export HOME="$WORK/home"; mkdir -p "$HOME" # never touch real ~/.neuron
export ENGRAM_DATA_DIR="$DATA"
export ENGRAM_WAL_SYNC=always
unset ENGRAM_STORE
fail=0
echo "== compiling harness (gcc: el_runtime.c + engram_store.c + test_m3_parity.c) =="
gcc -O1 -std=c11 -I "$INC" "$HERE/test_m3_parity.c" "$RT" "$ST" -lcurl -o "$BIN" 2>"$WORK/cc.log"
if [ $? -ne 0 ]; then echo "COMPILE FAILED:"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; fi
grep -i warning "$WORK/cc.log" | grep -iE 'engram_store|eg_store|eg_load|scan_nodes|scan_edges' && echo "(warnings in M3 code above)" || true
echo
echo "== 0) default-OFF: flag unset leaves the store untouched =="
( unset ENGRAM_STORE; "$BIN" offcheck "$DATA" )
[ $? -ne 0 ] && { echo "FAIL: offcheck"; fail=1; }
[ -e "$DATA/neuron.egm" ] && { echo "FAIL: neuron.egm created while flag OFF"; fail=1; } \
|| echo " ok: no neuron.egm created with flag OFF"
echo
echo "== 1) seed (ENGRAM_STORE unset): build graph, save snapshot.json, activate =="
( unset ENGRAM_STORE; "$BIN" seed "$DATA" ) || { echo "FAIL: seed"; fail=1; }
echo
echo "== 2) on (ENGRAM_STORE=1): import snapshot.json ONCE -> neuron.egm, resident-load, activate =="
ENGRAM_STORE=1 "$BIN" on "$DATA" || { echo "FAIL: on"; fail=1; }
[ -e "$DATA/neuron.egm" ] && echo " ok: neuron.egm created by import" || { echo "FAIL: neuron.egm missing"; fail=1; }
echo
echo "== 3) reboot (ENGRAM_STORE=1, snapshot.json DELETED): must load from neuron.egm, never JSON =="
rm -f "$DATA/snapshot.json"
ENGRAM_STORE=1 "$BIN" reboot "$DATA" || { echo "FAIL: reboot"; fail=1; }
echo
echo "== 4) parity comparison (modulo ordering) =="
python3 - "$DATA" <<'PY'
import json, sys, os
d = sys.argv[1]
def load(name):
with open(os.path.join(d, name)) as f: return json.load(f)
def norm_graph(g):
nodes = sorted(g.get("nodes", []), key=lambda n: n.get("id",""))
edges = sorted(g.get("edges", []), key=lambda e: e.get("id",""))
layers= sorted(g.get("layers", []), key=lambda l: l.get("layer_id",0))
return {"nodes":nodes, "edges":edges, "layers":layers}
def act_ids(a):
# list of (node id, promoted); robust set + ordered list
seq = [(e.get("node",{}).get("id",""), int(e.get("promoted",0))) for e in a]
return seq
rc = 0
snap = norm_graph(load("snapshot.json") if os.path.exists(os.path.join(d,"snapshot.json")) else load("off_graph.json"))
off = norm_graph(load("off_graph.json"))
on = norm_graph(load("on_graph.json"))
rebt = norm_graph(load("reboot_graph.json"))
def cmp(label, a, b):
global rc
if a == b:
print(f" PASS: {label} (nodes={len(a['nodes'])} edges={len(a['edges'])} layers={len(a['layers'])})")
else:
rc = 1
print(f" FAIL: {label}")
for k in ("nodes","edges","layers"):
if a[k] != b[k]:
print(f" {k}: {len(a[k])} vs {len(b[k])}")
for x,y in zip(a[k], b[k]):
if x != y:
print(f" first diff:\n A={json.dumps(x)[:300]}\n B={json.dumps(y)[:300]}")
break
cmp("graph: ENGRAM_STORE=1 (export) == ENGRAM_STORE=0 (JSON path)", on, off)
cmp("round-trip: snapshot.json seed == store export (on_graph)", on, off) # off_graph==snapshot save
cmp("reboot from neuron.egm (no JSON) == on-path store", rebt, on)
offa = act_ids(load("off_act.json"))
ona = act_ids(load("on_act.json"))
if set(offa) == set(ona):
print(f" PASS: activation result set identical (off={len(offa)} on={len(ona)} entries)")
if offa == ona:
print(" (and identical ordering/promotion sequence)")
else:
print(" (same set; ordering differs only where scores tie — reporting honestly)")
else:
rc = 1
print(" FAIL: activation result set differs")
print(f" off-only: {set(offa)-set(ona)}")
print(f" on-only: {set(ona)-set(offa)}")
sys.exit(rc)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== 5) ASan+UBSan build, exercise M3 scan/boot/hooks (leaks off — harness intentionally leaks el_strdup) =="
SANBIN="$WORK/m3.san"
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_m3_parity.c" "$RT" "$ST" -lcurl -o "$SANBIN" 2>"$WORK/san_cc.log"
if [ $? -ne 0 ]; then echo " SAN COMPILE FAILED:"; tail -20 "$WORK/san_cc.log"; fail=1; else
export ASAN_OPTIONS=detect_leaks=0
DATA2="$WORK/data2"; mkdir -p "$DATA2"
( unset ENGRAM_STORE; "$SANBIN" seed "$DATA2" ) >/dev/null 2>"$WORK/san_run.log" && \
ENGRAM_STORE=1 "$SANBIN" on "$DATA2" >/dev/null 2>>"$WORK/san_run.log" && \
{ rm -f "$DATA2/snapshot.json"; ENGRAM_STORE=1 "$SANBIN" reboot "$DATA2" >/dev/null 2>>"$WORK/san_run.log"; }
if grep -qiE 'runtime error|AddressSanitizer|UndefinedBehavior|ERROR: ' "$WORK/san_run.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san_run.log" | head; fail=1
else
echo " ok: ASan+UBSan clean across seed/on/reboot (scan, boot, resident-load, mutation hooks)"
fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "================ M3 PARITY GATE: PASS ================"; else echo "================ M3 PARITY GATE: FAIL ================"; fi
rm -rf "$WORK"
exit $fail
-137
View File
@@ -1,137 +0,0 @@
#!/usr/bin/env bash
# M7 index-driven-traversal gate. Pure C harness (NOT elb/elc): links the real
# el_runtime.c engram builtins + engram_store.c and drives ENGRAM_STORE off vs on.
# Proves (1) byte-identical activation parity flag-on == flag-off across a
# mutating query sequence, and (2) the O(E)-rebuild cost is eliminated flag-on.
# Writes ONLY under a throwaway /tmp dir with a throwaway HOME.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
RT="$HERE/../../lang/runtime/el_runtime.c"
ST="$HERE/../../lang/runtime/engram_store.c"
INC="$HERE/../../lang/runtime"
WORK="$(mktemp -d /tmp/engram-m7-XXXXXX)"
DATA="$WORK/data"; mkdir -p "$DATA"
BIN="$WORK/m7"
export HOME="$WORK/home"; mkdir -p "$HOME" # never touch real ~/.neuron
# Hermetic: point the embedder at a guaranteed-refused endpoint so eg_embed_fetch
# fails fast, the circuit breaker opens, and cosq is deterministically absent in
# EVERY run (no dependence on whether a dev Ollama happens to be listening). This
# makes the byte-identical parity comparison reproducible and non-flaky.
export EL_EMBED_URL="http://127.0.0.1:1/api/embeddings"
unset ENGRAM_STORE
fail=0
echo "== compiling harness (gcc: el_runtime.c + engram_store.c + test_m7_traversal.c) =="
gcc -O2 -std=c11 -I "$INC" "$HERE/test_m7_traversal.c" "$RT" "$ST" -lcurl -lm -o "$BIN" 2>"$WORK/cc.log"
if [ $? -ne 0 ]; then echo "COMPILE FAILED:"; cat "$WORK/cc.log"; rm -rf "$WORK"; exit 1; fi
echo " ok: compiled"
echo
echo "== 1) PARITY: index-driven (M7 incremental) activation must be IDENTICAL to the"
echo " full-rebuild scan path — proven under one identical ENGRAM_STORE=1 state,"
echo " so the ONLY variable is how per-node adjacency is maintained."
echo " (compared on deterministic fields: node label + activation_strength +"
echo " working_memory_weight + epistemic_confidence + hops + promoted, IN ORDER;"
echo " node id/timestamps are per-run random and are intentionally excluded.)"
( unset ENGRAM_STORE; "$BIN" parity-off "$DATA" ) || { echo "FAIL: parity-off run"; fail=1; }
ENGRAM_STORE=1 "$BIN" parity-on-rebuild "$DATA" || { echo "FAIL: parity-on-rebuild run"; fail=1; }
ENGRAM_STORE=1 "$BIN" parity-on-incr "$DATA" || { echo "FAIL: parity-on-incr run"; fail=1; }
python3 - "$DATA" <<'PY' || fail=1
import json, sys, os
d = sys.argv[1]
def proj(prefix, i):
a = json.load(open(os.path.join(d, f"{prefix}_act{i}.json")))
out = []
for e in a:
n = e.get("node", {})
out.append([n.get("label",""),
e.get("activation_strength"), e.get("working_memory_weight"),
e.get("epistemic_confidence"), e.get("hops"), e.get("promoted")])
return out
def compare(label, pa, pb, gate):
rc = 0
for i in (1,2,3,4):
a, b = proj(pa, i), proj(pb, i)
if a == b:
print(f" #{i} identical (entries={len(a)}, promoted={sum(1 for r in a if r[5])})")
else:
if gate: rc = 1
print(f" #{i} DIFFERS ({'FAIL' if gate else 'note'})")
for x,y in zip(a,b):
if x != y:
print(f" first diff:\n {pa}={x}\n {pb}={y}"); break
if len(a) != len(b): print(f" length: {pa}={len(a)} {pb}={len(b)}")
print(f" {'PASS' if rc==0 else 'FAIL'}: {label}")
return rc
print(" [CORE M7 GATE] flag-on incremental index == flag-on forced full rebuild:")
rc1 = compare("index-driven activation == full-rebuild scan (same flag state)",
"onincr", "onrb", gate=True)
print(" [context] flag-on incremental index vs flag-off scan path (today's behavior):")
rc2 = compare("M7 (flag-on) == flag-off scan path", "onincr", "off", gate=False)
print(" [context] flag-off scan vs flag-on forced rebuild (isolates any pre-existing")
print(" flag-on/off float difference, INDEPENDENT of M7's incremental path):")
rc3 = compare("flag-off == flag-on (both rebuild path)", "off", "onrb", gate=False)
sys.exit(rc1) # only the core M7 equivalence gates the result
PY
echo
echo "== 2) PERF: ~13k nodes / 43k edges, 200 (add-edge + activate) iterations =="
NODES=13000; EDGES=43000; ITERS=120
( unset ENGRAM_STORE; "$BIN" perf off "$DATA" "$NODES" "$EDGES" "$ITERS" ) | tee "$WORK/perf_off.txt"
[ ${PIPESTATUS[0]} -ne 0 ] && { echo "FAIL: perf off"; fail=1; }
ENGRAM_STORE=1 "$BIN" perf on "$DATA" "$NODES" "$EDGES" "$ITERS" | tee "$WORK/perf_on.txt"
[ ${PIPESTATUS[0]} -ne 0 ] && { echo "FAIL: perf on"; fail=1; }
python3 - "$WORK/perf_off.txt" "$WORK/perf_on.txt" <<'PY'
import re, sys
def parse(f):
t = open(f).read()
def g(k):
m = re.search(k+r'=([\d.]+)', t); return float(m.group(1)) if m else 0.0
return {'rw': g('rebuild_edge_work'), 'rb': g('rebuilds'), 'ap': g('incr_appends'),
'loop_s': g('loop='), 'maint': g('adj_maint'),
'perq': g('per_query')}
off, on = parse(sys.argv[1]), parse(sys.argv[2])
def ratio(a,b): return (a/b) if b else float('inf')
print()
print(f" ADJACENCY TRAVERSAL COST (the metric M7 changes):")
print(f" edge-touches in rebuilds: off={off['rw']:.0f} on={on['rw']:.0f} "
f"({ratio(off['rw'],on['rw']):.0f}x fewer on)")
print(f" full O(E) rebuilds: off={off['rb']:.0f} on={on['rb']:.0f}")
print(f" incremental O(1) appends: off={off['ap']:.0f} on={on['ap']:.0f}")
print(f" adjacency-maint wall-time: off={off['maint']:.4f}s on={on['maint']:.4f}s "
f"({ratio(off['maint'],on['maint']):.1f}x faster on)")
print(f" END-TO-END per-query time: off={off['perq']:.2f}ms on={on['perq']:.2f}ms")
print(f" (per-query is dominated by activation's O(N) node scoring over 13k nodes,")
print(f" which M7 does not touch; the delta is the eliminated rebuild time.)")
ok = on['rw'] < off['rw'] and on['maint'] < off['maint'] and on['rb'] < off['rb']
print(" PASS: flag-on eliminates the O(E) per-query rebuild (fewer edge-touches, less maint time)"
if ok else " FAIL: expected fewer edge-touches AND less adjacency-maint time on flag-on")
sys.exit(0 if ok else 1)
PY
[ $? -ne 0 ] && fail=1
echo
echo "== 3) ASan+UBSan clean across parity + a small perf loop (leaks off — harness intentionally leaks el_strdup) =="
SANBIN="$WORK/m7.san"
gcc -O1 -g -std=c11 -fsanitize=address,undefined -fno-sanitize-recover=undefined \
-I "$INC" "$HERE/test_m7_traversal.c" "$RT" "$ST" -lcurl -lm -o "$SANBIN" 2>"$WORK/san_cc.log"
if [ $? -ne 0 ]; then echo " SAN COMPILE FAILED:"; tail -20 "$WORK/san_cc.log"; fail=1; else
export ASAN_OPTIONS=detect_leaks=0
D2="$WORK/data2"; mkdir -p "$D2"
( unset ENGRAM_STORE; "$SANBIN" parity-off "$D2" ) >/dev/null 2>"$WORK/san_run.log" && \
ENGRAM_STORE=1 "$SANBIN" parity-on-rebuild "$D2" >/dev/null 2>>"$WORK/san_run.log" && \
ENGRAM_STORE=1 "$SANBIN" parity-on-incr "$D2" >/dev/null 2>>"$WORK/san_run.log" && \
( unset ENGRAM_STORE; "$SANBIN" perf off "$D2" 1500 5000 40 ) >/dev/null 2>>"$WORK/san_run.log" && \
ENGRAM_STORE=1 "$SANBIN" perf on "$D2" 1500 5000 40 >/dev/null 2>>"$WORK/san_run.log"
if grep -qiE 'runtime error|AddressSanitizer|UndefinedBehavior|ERROR: ' "$WORK/san_run.log"; then
echo " FAIL: sanitizer findings:"; grep -iE 'runtime error|Sanitizer|ERROR' "$WORK/san_run.log" | head; fail=1
else
echo " ok: ASan+UBSan clean across parity + perf (rebuild + incremental append + BFS)"
fi
fi
echo
if [ "$fail" -eq 0 ]; then echo "================ M7 TRAVERSAL GATE: PASS ================"; else echo "================ M7 TRAVERSAL GATE: FAIL ================"; fi
rm -rf "$WORK"
exit $fail
-23
View File
@@ -1,23 +0,0 @@
#!/bin/sh
# Build + RUN the REASONING-layer tests (engram_reason.c): closed-form constructed
# cases for ANALOGY / INDUCTION / ABDUCTION / CAUSAL / PLANNING, each composing the
# §5 geometry OPERATORS (engram_geometry.c). 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 (memory-safety is size-independent).
set -e
HERE=$(cd "$(dirname "$0")" && pwd)
RT="$HERE/../../lang/runtime"
CC=${CC:-cc}
SRC="$HERE/test_reason.c $RT/engram_reason.c $RT/engram_geometry.c $RT/engram_store.c $RT/engram_vindex.c"
WARN="-std=c11 -Wall -Wextra"
TMP=$(mktemp -d)
echo "### PASS 1: PERF (optimised, un-sanitised) — functional gate"
$CC $WARN -O2 -I"$RT" $SRC -lm -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 -o "$TMP/safe"
ASAN_OPTIONS=${ASAN_OPTIONS:-detect_leaks=0} UBSAN_OPTIONS=halt_on_error=1 "$TMP/safe"
-13
View File
@@ -1,13 +0,0 @@
#!/usr/bin/env bash
# M1 paged-store gate. Pure C (NOT elb/elc). Writes only under /tmp.
set -e
HERE="$(cd "$(dirname "$0")" && pwd)"
SRC="$HERE/../../lang/runtime/engram_store.c"
BIN="/tmp/test_store.$$"
echo "compiling: gcc test_store.c engram_store.c"
gcc -O2 -Wall -Wextra -std=c11 "$HERE/test_store.c" "$SRC" -o "$BIN"
"$BIN"
rc=$?
rm -f "$BIN"
rm -rf /tmp/engram-store-test-*
exit $rc
-24
View File
@@ -1,24 +0,0 @@
#!/bin/sh
# Build + RUN the VERIFIER-layer tests (engram_verify.c): closed-form constructed
# cases for GROUNDING (anti-hallucination) and CONSISTENCY (polarity/negation
# inversion + geometric contradiction), each composing the reasoning point-fit
# (engram_reason.c) and the §5 geometry OPERATORS (engram_geometry.c). 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 (memory-safety is size-independent).
set -e
HERE=$(cd "$(dirname "$0")" && pwd)
RT="$HERE/../../lang/runtime"
CC=${CC:-cc}
SRC="$HERE/test_verify.c $RT/engram_verify.c $RT/engram_reason.c $RT/engram_geometry.c $RT/engram_store.c $RT/engram_vindex.c"
WARN="-std=c11 -Wall -Wextra"
TMP=$(mktemp -d)
echo "### PASS 1: PERF (optimised, un-sanitised) — functional gate"
$CC $WARN -O2 -I"$RT" $SRC -lm -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 -o "$TMP/safe"
ASAN_OPTIONS=${ASAN_OPTIONS:-detect_leaks=0} UBSAN_OPTIONS=halt_on_error=1 "$TMP/safe"
-25
View File
@@ -1,25 +0,0 @@
#!/bin/sh
# Build + RUN the M8 HNSW vector-index tests. Pure C11 (gcc/cc), stdlib + libm
# only. This is a standalone C module — NOT folded through elb/elc.
#
# Two passes:
# 1. PERF — optimised (-O2, no sanitizer): the real recall@10 gate + speedup
# numbers at full size (N=5000 recall, N=5000/20000 speedup).
# 2. SAFETY — ASan + UBSan on the same suite at reduced size (VINDEX_QUICK=1);
# memory-safety is size-independent, so this stays fast.
set -e
HERE=$(cd "$(dirname "$0")" && pwd)
RT="$HERE/../../lang/runtime"
CC=${CC:-cc}
SRC="$HERE/test_vindex.c $RT/engram_vindex.c $RT/engram_store.c"
WARN="-std=c11 -Wall -Wextra"
TMP=$(mktemp -d)
echo "### PASS 1: PERF (optimised, un-sanitised) — recall gate + speedup"
$CC $WARN -O2 -I"$RT" $SRC -lm -o "$TMP/perf"
"$TMP/perf"
echo
echo "### PASS 2: SAFETY (ASan/UBSan, reduced size)"
$CC $WARN -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer -I"$RT" $SRC -lm -o "$TMP/safe"
VINDEX_QUICK=1 ASAN_OPTIONS=${ASAN_OPTIONS:-detect_leaks=0} UBSAN_OPTIONS=halt_on_error=1 "$TMP/safe"
-14
View File
@@ -1,14 +0,0 @@
#!/usr/bin/env bash
# M2 WAL + checkpoint + recovery gate. Pure C (NOT elb/elc). Writes only under /tmp.
# Recovery tests use ENGRAM_WAL_SYNC=always so every WAL record is durable at crash.
set -e
HERE="$(cd "$(dirname "$0")" && pwd)"
SRC="$HERE/../../lang/runtime/engram_store.c"
BIN="/tmp/test_wal_store.$$"
echo "compiling: gcc test_wal_store.c engram_store.c"
gcc -O2 -Wall -Wextra -std=c11 "$HERE/test_wal_store.c" "$SRC" -o "$BIN"
ENGRAM_WAL_SYNC=always "$BIN"
rc=$?
rm -f "$BIN"
rm -rf /tmp/engram-wal-test-*
exit $rc
-16
View File
@@ -1,16 +0,0 @@
#!/usr/bin/env bash
# WAL unit + integration + crash-fuzz gate. Throwaway HOME/dirs only.
set -e
HERE="$(cd "$(dirname "$0")" && pwd)"
REL="$HERE/../../lang/runtime"
cc -O2 -fbracket-depth=1024 -Wno-parentheses-equality -I"$REL" \
"$HERE/test_wal.c" -lcurl -lpthread -o /tmp/test_wal
HOME=/tmp/engram-throwaway-home /tmp/test_wal
# Fail-loud data-dir check (must exit 1 with a FATAL line):
cat > /tmp/test_failloud.c <<'C'
#include "el_runtime.c"
int main(void){ unsetenv("ENGRAM_DATA_DIR"); unsetenv("HOME");
engram_resolve_data_dir(); printf("REACHED\n"); return 0; }
C
cc -O2 -fbracket-depth=1024 -Wno-parentheses-equality -I"$REL" /tmp/test_failloud.c -lcurl -lpthread -o /tmp/test_failloud
if env -u HOME -u ENGRAM_DATA_DIR /tmp/test_failloud; then echo "FAIL: should have exited"; exit 1; else echo "[PASS] fail-loud exit on unresolvable HOME"; fi

Some files were not shown because too many files have changed in this diff Show More