runtime: transduction is a language concern, so move it into the language #144
Reference in New Issue
Block a user
Delete Branch "feat/el-geometry-transduce"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this is
#141 let signal enter as geometry and it worked. It was also placed at the consumer, and said so in its own commit message. This is that correction: transduction moves out of the engram and into the el language.
Why it was placed wrong (three defects, all placement)
It sat in the engram. Ingest is a language concern — every el program touching any modality needs it, and the engram is merely one el program that happens to hold a graph. The geometry surface is now defined in
el_runtime.cimmediately above the engram section and depends on nothing inside it. You can delete the entire engram and geometry still enters el. The file ordering is the architectural statement.It marshalled the vector as a hex STRING, because el had no first-class geometry value — reintroducing text as the transport medium one layer below the problem being fixed. Geometry is now an el value: a magic-tagged heap object carried in
el_val_t, same discipline asList/Map. Hex survives only as an adapter at the edge, which is all an encoding should ever be.It needed an arbitrary
dim <= 8192bound, purely to size an allocation from a caller's claim about a string's length. A value carries its own width, so the width is now derived and never asserted. The bound is gone, not raised — there is nothing left to validate. The only remaining failure is the allocation itself.The language surface (none of it engram-prefixed)
Geometryneeds zero codegen change — but not for the reason originally assumed.el_type_to_cis dead code (never called anywhere in the compiler); the shippedelcdiscards type annotations entirely and represents every value asel_val_t.GeometryfollowsCalendar/Instant: an opaque boxed pointer annotated as a bare identifier.Realizers are declarable in el — this is what makes the move real rather than nominal
Registration resolves a name with
dlsymagainst the running binary — the identical mechanismhttp_set_handleralready relies on, because every elfn name(...)compiles to a global C symbol with that exact name. So an ordinary el function IS a realizer, and a new modality needs no runtime patch.Proven end to end in
lang/examples/transduce.el(36 checks, exits non-zero on any failure; negative-control verified — deliberately breaking one assertion does fail it). The el-defined realizer is registered by name,transducedispatches to it, and the signal demonstrably reaches it: distinct signals produce distinct geometry.A modality with no realizer transduces to nothing. There is deliberately no built-in realizer, not even for text — silently embedding a description of a signal and calling that perception is the exact defect this ends.
realizer_registeron an unresolvable name fails at wiring time rather than surfacing later as "this modality mysteriously produces nothing".What #141's API becomes: wrapped and deprecated
engram_node_set_emb(id, hex, dim)is now a thin wrapper overgeometry_from_f32le_hex+node_attach_geometry— literally that, nothing more. It is kept only because the runtime ships as an SDK asset and a downstream binary may already link the symbol. It is not kept because a hex string is an acceptable way to move geometry between two pieces of el; that was the defect.Its exact contract is preserved, negative cases included, and re-verified:
dim <= 0rejects, malformed hex rejects, length/dim disagreement rejects, unknown id rejects. The difference is thatdimis now an assertion checked against a width the Geometry already knows, rather than the authority the allocation trusted.Wire compatibility
POST /api/nodeswithembis unchanged — production clients speak it. Hex is decoded exactly once, at the edge, into aGeometry; everything below that line moves geometry.dimis now an assertion about the vector rather than the source of its width, so a disagreement is a rejected ingest, not a silent reinterpretation. Omittingdimmeans "trust the vector", which is the honest default.ingest.el:
transducerenamed totransduce_manifoldMechanically it had to yield the name — duplicate C symbol, a hard
conflicting types for 'transduce'compile error, measured not anticipated. But it was never signal→geometry: it chunks already-extracted content into a node+edge manifold, one layer up, and had taken the name belonging to the primitive underneath it. Behaviour unchanged.Properties from #141 preserved — each re-measured, not assumed
Against a scratch engram on
:8971(prod:8742never touched; PID confirmed against the freshly built binary before every probe, per the stale-binary trap that nearly produced a false result last time):dimomittedemb_set:1, read-backemb_dim:64 embedded:trueemb_set:0, read-backemb_dim:0 embedded:falseemb_set:0emb_set:0dimdisagrees (claims 32, sends 64)emb_set:0dimagrees (claims 64, sends 64)emb_set:1,emb_dim:64n->emb_dim == dimat four sites, so a 64-dim voice vector is durable and addressable without perturbing the 768-dim canonical index. Search and health verified on the mixed-dim graph.embed_backfill— after backfill the 64-dim voice node was still 64-dim while the text control acquired 768. A realizer's vector is never overwritten by a text-derived one.emb_dimandembedded.Two compiler landmines found by reading the generated C
Both documented at their sites. Neither is caused by this change; both would have bitten it.
elclowersa == btostr_equnless both operand names are in the per-function int-name set — and that registration does not propagate into a nested if-expression block. The first cut of the ingest path emittedstr_eq(claimed, got), i.e.strcmpon two integers reinterpreted as pointers: a segfault on the first geometry-bearing request. It built cleanly. Only reading the emitted C caught it. Fixed by moving the comparison into a function whose parameters are: Int(which does register reliably), and verified in the output.+lowers to string concat when either operand is a user-defined call, so afails + check(...)tally printed4343632752— a pointer — while every check passed. The checks were right; the tally was lying.Not done / known state
lang/tests/native/test_transduce.eldoes not run. The shippedelcemits calls to__el_reg_count/__el_reg_invoke/ etc. without emitting their definitions, so every native test fails to link —test_math.elincluded, on unmodifieddev. Pre-existing and unrelated; the test file is committed in the conventional form for when the harness is repaired, andlang/examples/transduce.elis the runnable proof in the meantime.__-prefixedel_seed.cwrappers for the new builtins (step 2 of thelang/AGENTS.mdrecipe). Not needed by anything here —elcemits plain names — and adding them risks a link configuration I could not test.builtin_arity/is_int_callentries incodegen.el. Deliberate: those are optional guards, and touchingcodegen.elforces anelcrebuild plus a self-host fixpoint check. Consequence:geometry_dim(g) == nwritten inline will mis-lower — hencewidth_agrees()and the documented comparison discipline.signal+modalityroute, because with no realizer registered it would be a dead route — better to say so than to ship one.Build
engram, ingest, and the transduce example all build clean locally on this branch, rebased onto
devata6cef4b(#143), and every measurement above was re-run after that rebase.