9a13547fe2
scripts/check-single-runtime.sh guards against el_runtime.c being COPIED — it
was written after a lagging fork shipped to prod and dropped learned hebb edges.
Nothing guarded against it GROWING. So it grew: 10,607 -> 20,527 lines, 94% in
3.5 months, the whole time under an explicit commit-message promise that it was
a temporary shim about to be deleted.
Worse, the copy guard was never wired in. Its own footer described the CI
wire-in as a TODO, and the TODO had never been done — the script existed but ran
nowhere, in no workflow and in no hook, so it had caught nothing for as long as
it has been in the tree. A guard that does not run is a comment.
This adds the missing guard and runs both.
* lang/runtime/BUDGET — a RATCHET, not a limit. max_lines is set at the
current 20,527 with NO headroom: the file cannot grow by one line. A second
cap, max_engram_fns (279), counts top-level engram_/eg_/cog_ definitions in
it — ~47.5% of the file is engram code and engram already owns six sibling
.c files, so this is the scoreboard for moving it out. Both may only go DOWN.
* scripts/check-runtime-growth.sh — enforces the ratchet, and three
invariants that keep the multi-file runtime honest: every .c in
lang/runtime/ is either in SOURCES or explicitly platform-optional (an
unaccounted .c is compiled by nothing and is silently dead); install.sh's
hardcoded download list matches SOURCES (it cannot call the helper — it
runs where there is no checkout — so that copy is checked, not trusted);
and an advisory nudge to lower the budget when you have earned it.
* Both guards now run as early steps in ci-dev.yaml, ci-stage.yaml and
sdk-release.yaml, and in .githooks/pre-commit.
The failure message is the point. The guard that existed said what was wrong but
not where the code should go, which makes it easy to "fix" by arguing with the
guard. This one names the destination: the concern-owning .c, or a new .c plus
one line in SOURCES, or c_source in a program's manifest.el — and it prints the
`nm` command that proves placement is link-time and that the shipped compiler
already links from ten translation units. Every runtime file except el_runtime.c
is deliberately uncapped, because that is where code is supposed to go.
Proven with negative controls, per lang/AGENTS.md step 5 — each shown FAILING:
* +1 line to el_runtime.c -> FAIL (20528/20527)
* +1 engram fn, net-zero lines -> FAIL (280/279)
* a new unaccounted lang/runtime/*.c -> FAIL
* engram_store.c removed from install.sh -> FAIL, names the missing file
* el_runtime.c truncated to 20,000 lines -> PASS + "lower max_lines to 20000"
* baseline, tree unmodified -> OK, and both guards green
el_runtime.c is byte-identical after the controls; this commit changes zero
lines of it.
90 lines
3.2 KiB
Bash
Executable File
90 lines
3.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# El pre-commit hook: compile and run native tests before commit.
|
|
# Install once per clone: git config core.hooksPath .githooks
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT="$(git rev-parse --show-toplevel)"
|
|
LANG_DIR="$ROOT/lang"
|
|
RUNTIME="$LANG_DIR/runtime"
|
|
ELC="$LANG_DIR/dist/platform/elc"
|
|
|
|
# Runtime guards — catch drift and growth before they are committed, not in CI.
|
|
# check-single-runtime.sh : el_runtime.c must not be FORKED (a lagging copy
|
|
# shipped to prod and dropped learned hebb edges).
|
|
# check-runtime-growth.sh : el_runtime.c must not GROW (it is a 2026-05-03
|
|
# build shim that was never retired; see BUDGET).
|
|
echo "→ Runtime guards..."
|
|
bash "$ROOT/scripts/check-single-runtime.sh"
|
|
bash "$ROOT/scripts/check-runtime-growth.sh"
|
|
|
|
# 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: see 'Rebuilding the Compiler' in lang/AGENTS.md"
|
|
echo " (link \$($ROOT/scripts/el-runtime-sources.sh $RUNTIME) — NOT el_runtime.c alone)"
|
|
exit 0
|
|
fi
|
|
|
|
# The runtime is MULTI-FILE (lang/runtime/SOURCES). This hook used to link
|
|
# "$RUNTIME/el_runtime.c" alone with stderr sent to /dev/null — so once
|
|
# el_runtime.c started calling into the engram siblings, every native test
|
|
# reported as FAILED with the real `ld` error invisible. Build the whole set
|
|
# once into an archive, then link each test against it.
|
|
|
|
# macOS: Homebrew openssl@3 is not on the default include/lib search path, so
|
|
# without these the link fails on -lssl/-lcrypto. Empty on Linux/CI.
|
|
SSL_INC=""
|
|
SSL_LIB=""
|
|
if command -v brew >/dev/null 2>&1 && OSSL="$(brew --prefix openssl@3 2>/dev/null)" && [ -n "$OSSL" ]; then
|
|
SSL_INC="-I$OSSL/include"
|
|
SSL_LIB="-L$OSSL/lib"
|
|
fi
|
|
|
|
echo "→ Building runtime (compile-once, link-many)..."
|
|
HOOK_LIB="/tmp/el_hook_libel.a"
|
|
HOOK_OBJ="/tmp/el_hook_obj"
|
|
rm -rf "$HOOK_OBJ" && mkdir -p "$HOOK_OBJ"
|
|
if ! for src in $("$ROOT/scripts/el-runtime-sources.sh" --check "$RUNTIME"); do
|
|
gcc -O2 -c -I "$RUNTIME" $SSL_INC "$src" -o "$HOOK_OBJ/$(basename "${src%.c}").o" || exit 1
|
|
done; then
|
|
echo "✗ Pre-commit failed: the runtime does not compile."
|
|
echo " Re-run without 2>/dev/null to see the error:"
|
|
echo " gcc -O2 -c -I $RUNTIME \$($ROOT/scripts/el-runtime-sources.sh $RUNTIME)"
|
|
exit 1
|
|
fi
|
|
ar rcs "$HOOK_LIB" "$HOOK_OBJ"/*.o
|
|
|
|
echo "→ Running El native tests..."
|
|
PASS=0
|
|
FAIL=0
|
|
FAILED_TESTS=""
|
|
|
|
for test_file in "$LANG_DIR"/tests/native/test_*.el; do
|
|
name=$(basename "$test_file" .el)
|
|
tmp_c="/tmp/el_hook_${name}.c"
|
|
tmp_bin="/tmp/el_hook_${name}"
|
|
|
|
if "$ELC" --test "$test_file" > "$tmp_c" 2>/dev/null \
|
|
&& gcc -O2 -I "$RUNTIME" $SSL_INC $SSL_LIB "$tmp_c" "$HOOK_LIB" \
|
|
-lcurl -lssl -lcrypto -lpthread -lm -o "$tmp_bin" 2>/dev/null \
|
|
&& "$tmp_bin" 2>/dev/null; then
|
|
PASS=$((PASS + 1))
|
|
else
|
|
echo " ✗ $name"
|
|
FAIL=$((FAIL + 1))
|
|
FAILED_TESTS="$FAILED_TESTS $name"
|
|
fi
|
|
done
|
|
|
|
echo " $PASS passed, $FAIL failed"
|
|
|
|
if [ "$FAIL" -gt 0 ]; then
|
|
echo ""
|
|
echo "✗ Pre-commit failed. Fix these tests before committing:$FAILED_TESTS"
|
|
exit 1
|
|
fi
|
|
|
|
echo "✓ All tests passed"
|
|
exit 0
|