Compare commits

..

1 Commits

Author SHA1 Message Date
Neuron 40653d2aa1 fix(codegen): emit the declared cgi identity — it was searched for in a list that cannot contain it
El SDK Release / build-and-release (pull_request) Failing after 10m39s
A cgi block is a top-level declaration, so codegen_streaming classifies it via
is_top_level_decl and releases it. The identity emission then searched
toplevel_exec_stmts for that same block. Declarations are excluded from that list by
construction, so the search could never succeed. A probe printed what it actually
saw for a program whose first statement is a cgi block: [Let, Expr]. It emitted
nothing, silently, with no diagnostic on any channel.

The code documented its own assumption — 'Since cgi blocks are rare and small, they
end up in toplevel_exec_stmts' — and that assumption was false.

Capture the declared values before the release and emit from them. The search is
deleted rather than repaired, so the failure mode is removed rather than relocated.

Proven discriminating (old fails, new passes):
  minimal cgi program, old   -> 0 el_cgi_init
  minimal cgi program, fixed -> el_cgi_init with all four declared values
  neuron soul, fixed         -> principal present in the compiled binary (0 before),
                                boots in 2s, interface 110 routes in / 110 out

Consequence: a binary now carries its declared identity as a compiled constant,
which is what the identity protocol requires. Whether the runtime surfaces it to
state_get("soul_principal") is unverified and separate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 13:36:51 -05:00
2 changed files with 53 additions and 91 deletions
+10 -64
View File
@@ -82,14 +82,8 @@ static _Thread_local ElArena _tl_arena = {NULL, 0, 0};
static _Thread_local int _tl_arena_active = 0; static _Thread_local int _tl_arena_active = 0;
/* Binary-safe fs_read length — set by fs_read, consumed by http_send_response. /* Binary-safe fs_read length — set by fs_read, consumed by http_send_response.
* Allows serving PNGs and other binary files without strlen truncation. * Allows serving PNGs and other binary files without strlen truncation. */
* PAIRED with the buffer pointer it describes: the length may only be applied static _Thread_local size_t _tl_fs_read_len = 0;
* to the exact buffer fs_read returned. Without the pairing, any handler that
* fs_read a file and then WRAPPED it into a larger response had that response
* truncated to the file's length (Content-Length lied AND the send stopped
* short) the safety-contact onboarding trap, 2026-07-17. */
static _Thread_local size_t _tl_fs_read_len = 0;
static _Thread_local const char* _tl_fs_read_buf = NULL;
static void el_arena_track(char* p) { static void el_arena_track(char* p) {
if (!_tl_arena_active || !p) return; if (!_tl_arena_active || !p) return;
@@ -107,8 +101,6 @@ static void el_arena_track(char* p) {
void el_request_start(void) { void el_request_start(void) {
_tl_arena.count = 0; _tl_arena.count = 0;
_tl_arena_active = 1; _tl_arena_active = 1;
_tl_fs_read_len = 0; /* never let a previous request's file length */
_tl_fs_read_buf = NULL; /* leak into this response's byte accounting */
} }
/* Called by http_worker after the El handler returns and the response is sent. /* Called by http_worker after the El handler returns and the response is sent.
@@ -1492,14 +1484,11 @@ static void http_send_response(int fd, const char* body) {
} }
const char* eff_body = is_envelope ? env_body : body; const char* eff_body = is_envelope ? env_body : body;
/* Use the real byte count from fs_read ONLY when this body IS the exact /* Use the real byte count from fs_read if available (handles binary files
* buffer fs_read returned (binary files with embedded null bytes PNG, * with embedded null bytes PNG, WOFF2, etc.). Fall back to strlen for
* WOFF2, etc.). Any other body wrapped, enveloped, or derived must be * normal text/JSON responses where _tl_fs_read_len is 0. */
* measured with strlen, or it is truncated/over-read to the file's size. */ size_t blen = (_tl_fs_read_len > 0) ? _tl_fs_read_len : strlen(eff_body);
size_t blen = (_tl_fs_read_len > 0 && eff_body == _tl_fs_read_buf)
? _tl_fs_read_len : strlen(eff_body);
_tl_fs_read_len = 0; /* consume — one-shot per response */ _tl_fs_read_len = 0; /* consume — one-shot per response */
_tl_fs_read_buf = NULL;
int head_only = _tl_http_head_only; int head_only = _tl_http_head_only;
JsonBuf hdrs; jb_init(&hdrs); JsonBuf hdrs; jb_init(&hdrs);
@@ -1579,22 +1568,11 @@ static void* http_worker(void* arg) {
const char* rs = EL_CSTR(r); const char* rs = EL_CSTR(r);
/* Copy response out BEFORE arena teardown. /* Copy response out BEFORE arena teardown.
* For binary files, _tl_fs_read_len holds the real byte count * For binary files, _tl_fs_read_len holds the real byte count
* use memcpy instead of strdup so null bytes are preserved. * use memcpy instead of strdup so null bytes are preserved. */
* The stored length applies ONLY when the response IS the exact size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
* fs_read buffer; a wrapped/derived response must use strlen or
* it gets truncated (or over-read) to the file's length. */
size_t rlen;
if (_tl_fs_read_len > 0 && rs && rs == _tl_fs_read_buf) {
rlen = _tl_fs_read_len; /* raw file bytes — binary-safe */
} else {
rlen = rs ? strlen(rs) : 0;
_tl_fs_read_len = 0; /* hint doesn't describe this body */
_tl_fs_read_buf = NULL;
}
response = malloc(rlen + 1); response = malloc(rlen + 1);
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; } if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
else if (response) { response[0] = '\0'; } else if (response) { response[0] = '\0'; }
if (_tl_fs_read_len > 0) _tl_fs_read_buf = response; /* hint follows the copy */
} else { } else {
response = el_strdup_persist("el-runtime: no http handler registered"); response = el_strdup_persist("el-runtime: no http handler registered");
} }
@@ -1844,20 +1822,10 @@ static void* http_worker_v2(void* arg) {
el_val_t hmap = http_build_headers_map(hdr_block ? hdr_block : ""); el_val_t hmap = http_build_headers_map(hdr_block ? hdr_block : "");
el_val_t r = h(EL_STR(dispatch_method), EL_STR(path), hmap, EL_STR(body)); el_val_t r = h(EL_STR(dispatch_method), EL_STR(path), hmap, EL_STR(body));
const char* rs = EL_CSTR(r); const char* rs = EL_CSTR(r);
/* Same pairing rule as the v1 worker: the fs_read length is only size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
* trustworthy for the exact buffer fs_read returned. */
size_t rlen;
if (_tl_fs_read_len > 0 && rs && rs == _tl_fs_read_buf) {
rlen = _tl_fs_read_len; /* raw file bytes — binary-safe */
} else {
rlen = rs ? strlen(rs) : 0;
_tl_fs_read_len = 0; /* hint doesn't describe this body */
_tl_fs_read_buf = NULL;
}
response = malloc(rlen + 1); response = malloc(rlen + 1);
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; } if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
else if (response) { response[0] = '\0'; } else if (response) { response[0] = '\0'; }
if (_tl_fs_read_len > 0) _tl_fs_read_buf = response; /* hint follows the copy */
el_release(hmap); el_release(hmap);
} else { } else {
response = el_strdup_persist( response = el_strdup_persist(
@@ -2055,7 +2023,6 @@ el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body) {
el_val_t fs_read(el_val_t pathv) { el_val_t fs_read(el_val_t pathv) {
const char* path = EL_CSTR(pathv); const char* path = EL_CSTR(pathv);
_tl_fs_read_len = 0; _tl_fs_read_len = 0;
_tl_fs_read_buf = NULL;
if (!path) return el_wrap_str(el_strdup("")); if (!path) return el_wrap_str(el_strdup(""));
FILE* f = fopen(path, "rb"); FILE* f = fopen(path, "rb");
if (!f) return el_wrap_str(el_strdup("")); if (!f) return el_wrap_str(el_strdup(""));
@@ -2067,7 +2034,6 @@ el_val_t fs_read(el_val_t pathv) {
size_t got = fread(buf, 1, (size_t)sz, f); size_t got = fread(buf, 1, (size_t)sz, f);
buf[got] = '\0'; buf[got] = '\0';
_tl_fs_read_len = got; /* store real byte count for binary-safe send */ _tl_fs_read_len = got; /* store real byte count for binary-safe send */
_tl_fs_read_buf = buf; /* ...valid ONLY for this exact buffer */
fclose(f); fclose(f);
return el_wrap_str(buf); return el_wrap_str(buf);
} }
@@ -3610,10 +3576,8 @@ el_val_t json_get_raw(el_val_t json_str, el_val_t key) {
const char* k = EL_CSTR(key); const char* k = EL_CSTR(key);
const char* p = json_find_key(json, k); const char* p = json_find_key(json, k);
/* Clear fs_read binary-length hint — result is a fresh null-terminated /* Clear fs_read binary-length hint — result is a fresh null-terminated
* string, not the raw file bytes, so Content-Length must use strlen. * string, not the raw file bytes, so Content-Length must use strlen. */
* (Kept although the pointer pairing now makes this redundant.) */
_tl_fs_read_len = 0; _tl_fs_read_len = 0;
_tl_fs_read_buf = NULL;
if (!p) return el_wrap_str(el_strdup("")); if (!p) return el_wrap_str(el_strdup(""));
const char* end = json_skip_value(p); const char* end = json_skip_value(p);
size_t n = (size_t)(end - p); size_t n = (size_t)(end - p);
@@ -7899,24 +7863,6 @@ el_val_t engram_get_node_json(el_val_t id) {
return el_wrap_str(jb_finish(&b)); return el_wrap_str(jb_finish(&b));
} }
/* Look up a node by exact label; returns its JSON or {}. Ported from the
* v1.0.0 release runtime needed by soul.el session continuity
* (conv_history_load / session_summary_write / emit_session_start_event). */
el_val_t engram_get_node_by_label(el_val_t label) {
const char* lbl = EL_CSTR(label);
if (!lbl || !*lbl) return el_wrap_str(el_strdup(""));
EngramStore* g = engram_get();
for (int64_t i = 0; i < g->node_count; i++) {
EngramNode* n = &g->nodes[i];
if (n->label && strcmp(n->label, lbl) == 0) {
JsonBuf b; jb_init(&b);
engram_emit_node_json(&b, n);
return el_wrap_str(b.buf);
}
}
return el_wrap_str(el_strdup(""));
}
el_val_t engram_search_json(el_val_t query, el_val_t limit) { el_val_t engram_search_json(el_val_t query, el_val_t limit) {
/* SPEC-SEARCH-UPGRADE 2026-07-14: same ranked BM25+recency core as /* SPEC-SEARCH-UPGRADE 2026-07-14: same ranked BM25+recency core as
* engram_search; transparent-layer identity filter enforced inside it. */ * engram_search; transparent-layer identity filter enforced inside it. */
+43 -27
View File
@@ -3626,6 +3626,24 @@ fn codegen_streaming(tokens: [Any], sigs: [Map<String, Any>], source: String) ->
let pos: Int = 0 let pos: Int = 0
let el_main_body: [Map<String, Any>] = native_list_empty() let el_main_body: [Map<String, Any>] = native_list_empty()
let toplevel_exec_stmts: [Map<String, Any>] = native_list_empty() let toplevel_exec_stmts: [Map<String, Any>] = native_list_empty()
// CGI IDENTITY CAPTURE (2026-08-09). A cgi block is a top-level DECLARATION, so
// the classifier below correctly excludes it from toplevel_exec_stmts and calls
// el_release on it. The identity emission further down then searched
// toplevel_exec_stmts for it a list that structurally can never contain it
// found nothing, and emitted nothing, silently. Measured: that search sees only
// [Let, Expr] for a program whose first statement is a cgi block.
// Fix: copy the values out BEFORE the release (strings, so no dangling reference)
// and emit from these. No search, so the failure mode is removed rather than moved.
let cgi_have: Bool = false
let cgi_name_v: String = ""
let cgi_did_v: String = ""
let cgi_prin_v: String = ""
let cgi_net_v: String = ""
let cgi_eng_v: String = ""
let cgi_has_did: Bool = false
let cgi_has_prin: Bool = false
let cgi_has_net: Bool = false
let cgi_has_eng: Bool = false
let has_toplevel_exec: Bool = false let has_toplevel_exec: Bool = false
let stream_running: Bool = true let stream_running: Bool = true
@@ -3736,6 +3754,20 @@ fn codegen_streaming(tokens: [Any], sigs: [Map<String, Any>], source: String) ->
if is_top_level_decl(stmt) { if is_top_level_decl(stmt) {
// Import, TypeDef, EnumDef, CgiBlock, ServiceBlock, ExternFn // Import, TypeDef, EnumDef, CgiBlock, ServiceBlock, ExternFn
// These are no-ops in codegen (forward decls already emitted) // These are no-ops in codegen (forward decls already emitted)
// except a CgiBlock, whose declared identity must survive
// this release to be emitted as a compiled constant.
if str_eq(sk, "CgiBlock") {
let cgi_have = true
let cgi_name_v = stmt["name"]
let cgi_did_v = stmt["dharma_id"]
let cgi_prin_v = stmt["principal"]
let cgi_net_v = stmt["network"]
let cgi_eng_v = stmt["engram"]
let cgi_has_did = stmt["has_dharma_id"]
let cgi_has_prin = stmt["has_principal"]
let cgi_has_net = stmt["has_network"]
let cgi_has_eng = stmt["has_engram"]
}
el_release(stmt) el_release(stmt)
} else { } else {
if str_eq(sk, "Let") { if str_eq(sk, "Let") {
@@ -3814,33 +3846,17 @@ fn codegen_streaming(tokens: [Any], sigs: [Map<String, Any>], source: String) ->
let sig2 = native_list_get(sigs, si2) let sig2 = native_list_get(sigs, si2)
let sk3: String = sig2["kind"] let sk3: String = sig2["kind"]
if str_eq(sk3, "cgi_block") { if str_eq(sk3, "cgi_block") {
// We need the full cgi_block data it was parsed by scan_fn_sigs // Emit from the values captured before the declaration was released.
// but scan only stored the name. For cgi_init we need dharma_id etc. // The previous implementation searched toplevel_exec_stmts, which by
// Since cgi blocks are rare and small, they end up in toplevel_exec_stmts. // construction never contains a declaration so it emitted nothing and
// Find the CgiBlock in toplevel_exec_stmts. // said nothing. See the capture block near toplevel_exec_stmts init.
let tes_n: Int = native_list_len(toplevel_exec_stmts) if cgi_have {
let tes_i: Int = 0 let arg_name2: String = "EL_STR(" + c_str_lit(cgi_name_v) + ")"
while tes_i < tes_n { let arg_did2: String = cgi_arg(cgi_did_v, cgi_has_did)
let tes = native_list_get(toplevel_exec_stmts, tes_i) let arg_prin2: String = cgi_arg(cgi_prin_v, cgi_has_prin)
let tes_k: String = tes["stmt"] let arg_net2: String = cgi_arg(cgi_net_v, cgi_has_net)
if str_eq(tes_k, "CgiBlock") { let arg_eng2: String = cgi_arg(cgi_eng_v, cgi_has_eng)
let cname2: String = tes["name"] emit_line(" el_cgi_init(" + arg_name2 + ", " + arg_did2 + ", " + arg_prin2 + ", " + arg_net2 + ", " + arg_eng2 + ");")
let cdid2: String = tes["dharma_id"]
let cprin2: String = tes["principal"]
let cnet2: String = tes["network"]
let ceng2: String = tes["engram"]
let has_did2: Bool = tes["has_dharma_id"]
let has_prin2: Bool = tes["has_principal"]
let has_net2: Bool = tes["has_network"]
let has_eng2: Bool = tes["has_engram"]
let arg_name2: String = "EL_STR(" + c_str_lit(cname2) + ")"
let arg_did2: String = cgi_arg(cdid2, has_did2)
let arg_prin2: String = cgi_arg(cprin2, has_prin2)
let arg_net2: String = cgi_arg(cnet2, has_net2)
let arg_eng2: String = cgi_arg(ceng2, has_eng2)
emit_line(" el_cgi_init(" + arg_name2 + ", " + arg_did2 + ", " + arg_prin2 + ", " + arg_net2 + ", " + arg_eng2 + ");")
}
let tes_i = tes_i + 1
} }
} }
let si2 = si2 + 1 let si2 = si2 + 1