This repository has been archived on 2026-05-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Will Anderson f4abfe6fdc feat: rename crates/ → vessels/ + add El ports per sub-vessel
Belated rename commit for foundation/el-ui — was missed in the
workspace-wide crates→vessels pass earlier today. Same structural
intent as the rename in the other repos: 'crates' is the Rust word,
'vessel' is El's, and the directory rename is the marker that this
slot holds an El buildable unit even if its current contents are
still Rust pending port.

Plus the El ports themselves — manifest.el + src/main.el per sub-
vessel (el-aop, el-auth, el-config, el-i18n, el-identity, el-layout,
el-platform, el-publish, el-secrets, el-services, el-style, el-ui-
compiler). The ui-compiler is a stub: elc only emits C right now;
generating browser-target JS/Wasm is the biggest open language gap
and gets its own project. Until then, el-ui-compiler emits a JS
module that throws elc.backend_missing so callers fail loudly.
Cross-repo path dependencies in Cargo.toml updated to vessels/.
2026-04-30 18:18:39 -05:00

29 KiB
Raw Permalink Blame History

el-ui Framework Specification

Version 1.1.0 — April 30, 2026


Overview

el-ui is a frontend framework where state is an Engram graph and reactivity is spreading activation. Every framework must answer the same question: what re-renders when state changes? el-ui's answer is: whatever the spreading activation algorithm determines is relevant.

Framework Reactivity model
React Virtual DOM diffing
Vue Dependency tracking (Proxy-based reactive primitives)
Svelte Compile-time static analysis
Solid Fine-grained signal-based subscriptions
el-ui Spreading activation over a typed state graph

The core insight: component state is represented as a graph of related nodes, and the same spreading activation algorithm used by the Engram knowledge engine determines which components need to update. Reactivity is not declared, tracked, or diffed — it is activated and propagated, exactly as associative memory works in biological neural networks.


1. Activation-Based Reactivity Model

1.1 The Model

Every piece of state in an el-ui application is a node in an in-browser Engram graph. Nodes have:

Field Type Description
id String (UUID) Node's permanent identity
type String 'state', 'route', or user-defined
name String State variable name
content Any Current value
importance Float [0, 1] Salience weight; boosted on update

Nodes are connected by directed edges with:

Field Type Description
weight Float [0, 1] Connection strength
relation String Semantic label of the relationship

1.2 Activation Formula

When state changes via setState(), spreading activation runs from the changed node through the graph:

strength = parent_strength × edge.weight × target.importance

This formula is identical to the Engram core spreading activation formula, with the semantic similarity factor (cosine_sim) omitted because the in-browser graph does not yet carry embedding vectors. In a future version connected to the Engram server, the full four-factor formula applies:

strength = parent_strength × edge.weight × target.importance × cosine_sim(query, target)

1.3 Why Multiplication, Not Addition

Addition allows many weak signals to accumulate into false relevance. The brain's associative memory is conjunctive: an activated path requires ALL its links to be strong. Multiplication enforces this. If any factor is near zero — weak edge, dormant node, or irrelevant connection — the path dies immediately. This is not a performance optimization; it is a semantic property of associative retrieval.

1.4 Pruning Threshold

Paths with activation strength below PRUNE_THRESHOLD (default: 0.01) are cut. This prevents exponential blowup in large state graphs and models the brain's attention filter. All nodes not reached above the threshold are not considered for re-render.

1.5 Importance Boost (Long-Term Potentiation Analog)

When a state node is updated, its importance increases by 0.1 (capped at 1.0):

node.importance = Math.min(1.0, node.importance + 0.1)

Recently-changed state becomes more salient — it activates more easily in future propagation. This mirrors long-term potentiation: frequently-used pathways become lower-resistance. State that changes often gains importance; state that never changes fades toward background salience.

1.6 Activation Surface

The activation surface is the set of nodes reached during a spreading activation pass, i.e., all nodes with strength > PRUNE_THRESHOLD. Components subscribed to any node in the activation surface receive update notifications and re-render.


2. Component Definition Syntax

Components are defined in .el files (shared extension with the El programming language). A component is a specialized el module with four optional sections.

2.1 Component Structure

component ComponentName {
    props {
        // prop declarations
    }

    state {
        // state declarations
    }

    fn methodName(param: Type) -> ReturnType {
        // method body
    }

    template {
        // HTML template
    }
}

All four sections are optional. Components with no template render an empty string. Multiple fn blocks are allowed; multiple props or state blocks are not (only the last one is used).

2.2 Props

Props are read-only inputs from the parent component.

props {
    label: String               // required prop
    variant: String = "primary" // optional with default
    disabled: Bool = false      // boolean with default
    onClick: Fn() -> Void       // function prop
}

Supported prop types: String, Int, Float, Bool, Fn(...) -> T.

Note: Array ([T]) and optional (T?) type syntax is not currently parsed by the compiler. The parser reads a single identifier as the type name.

Props are accessed in generated code as this._props_{name} (not this.props.name). In method and template bodies, the compiler exposes each prop as a local constant named {name}.

2.3 State

State declarations create nodes in the component's Engram graph. Every state variable requires an initial value.

state {
    count: Int = 0
    label: String = "hello"
    active: Bool = false
}

Each state variable becomes:

  1. A node in this._graph (seeded at construction time with type: 'state'), tracked by ID in this._stateNodes.
  2. A mirrored value in this._state for direct read access.
  3. A reactive binding: changing the value via setState() triggers activation and re-render.

In method and template bodies, the compiler exposes each state variable as a local constant named {name} (reading from this._state).

2.4 Methods

Methods are fn definitions inside the component body. State assignments in method bodies compile to setState() calls.

fn increment() -> Void {
    count = count + 1    // compiles to: __self.setState('count', count + 1)
}

The generated JavaScript method exposes all state variables as local const bindings before executing the body. The variable __self refers to the component instance.


3. Template Syntax

3.1 Interpolation

Any expression embedded in {expr}:

<h1>{count}</h1>
<p>{"Hello, " + name + "!"}</p>
<span>{active ? "on" : "off"}</span>

3.2 Event Binding

DOM events bound with on:event={handler}:

<button on:click={() => count = count + 1}>+</button>
<input on:input={(e) => value = e.target.value} />
<div on:mouseenter={() => hovered = true} on:mouseleave={() => hovered = false} />

Supported events: click, input, change, mouseenter, mouseleave, keydown, keyup, keypress, focus, blur, submit, mousedown, mouseup, dblclick, contextmenu.

The compiler emits data-el-{event} attributes on elements. The renderer binds handlers at mount time using new Function(...) evaluated in the component's scope (__self), and rebinds after each patch.

3.3 Dynamic Attributes

<div class={active ? "btn-active" : "btn"}></div>
<a href={currentUrl}></a>

3.4 Static Attributes

<div class="container"></div>
<input type="text" />

3.5 Boolean Attributes

The parser recognises a fixed set of boolean attribute names: disabled, checked, readonly, required, multiple, selected. These emit the attribute name when the expression is truthy, nothing when falsy.

<button disabled={isDisabled}>Submit</button>
<input checked={isChecked} />

A standalone attribute with no value (e.g., <button disabled>) is also parsed and treated as BoolAttr { expr: "true" }.

3.6 Component Invocation

Uppercase-initial tags are el-ui components; lowercase tags are HTML elements:

<Button label="Click me" variant="primary" onClick={() => handleClick()} />
<Counter initialValue={5} />
<UserCard name={currentUser.name} />

The compiler emits ${__self._child(ComponentClass, { prop: value })} for component references. Child components share the parent's activation graph via _child().

3.7 Conditional Rendering — {#if}

{#if loggedIn}
    <Dashboard />
{:else}
    <Login />
{/if}

Compiles to a ternary template literal expression: ${(condition) ? ...:...}.

3.8 List Rendering — {#each}

{#each users as user}
    <UserCard name={user.name} />
{/each}

The items expression must evaluate to an array. Compiles to ${(items).map((user) => ...).join('')}.

3.9 Semantic Activation — {#activate} (Novel Construct)

{#activate "recent premium subscribers" as results}
    <UserCard name={results.name} />
{/activate}

{#activate} runs a semantic query against the component's state graph at render time. The query string is a natural language description. The runtime:

  1. Calls this._graph.search("query") which performs a string-based substring match on node name and content fields.
  2. Sorts results by score (combination of name/content match and node importance).
  3. Renders the template body once for each result, binding the result as the variable name.

Compiles to:

${((this._graph.search("query")) || []).map((results) => `...template...`).join('')}

In v0.1, the search is string-based (substring match on name and content.toString()). In future versions connected to an Engram database, {#activate} will use embedding vectors and cosine similarity.

Note on business logic protection: The spec previously described AES-256-GCM encryption of {#activate} query strings in sealed production bundles. This feature is not implemented — there is no --target prod flag in the current CLI and no el seal command.


4. State Graph

4.1 Graph Structure

Each component instance owns an Engram graph (this._graph). State nodes are seeded at construction. The graph is shared between a component and its children via _child() — they operate on the same activation surface.

4.2 State Node Creation

const nodeId = graph.seed({ type: 'state', name: 'count', content: 0 });
// importance defaults to 0.5 if omitted

Returns a UUID string that permanently identifies the node.

4.3 Edge Creation

graph.connect(fromId, toId, { weight: 0.8, relation: 'derived' });

Connects two nodes with a directed edge. Higher weight = stronger activation path. Default weight is 1.0, default relation is 'related'.

4.4 Node Retrieval

const node = graph.get(nodeId);
// Returns: { id, type, name, content, importance, edges: [edgeId, ...] }

Note: the runtime method is graph.get(id), not graph.getNode(id).

4.5 State Update

this.setState('count', newValue);
// or directly:
this._graph.update(nodeId, newValue);

setState looks up the node by state variable name, calls this._graph.update() which:

  1. Updates content.
  2. Increments importance by 0.1 (capped at 1.0).
  3. Runs spreading activation from the changed node.
  4. Notifies all subscribers in the activation surface.
  5. Notifies direct subscribers on the changed node.

4.6 Activation Subscription

const unsubscribe = graph.subscribe(nodeId, (node) => {
    // called whenever this node enters the activation surface
});
unsubscribe(); // stop listening

Components subscribe to their state nodes in the constructor. When spreading activation reaches a subscribed node, the subscriber callback fires and the component re-renders via this._renderer.patch().

const results = graph.search("recent items", "state");
// Returns: [{ id, type, name, content, importance, score }] sorted by score
// nodeType argument is optional — filters by node.type if provided

In v0.1, search is string-based: name.includes(query) or content.toString().includes(query). Score is computed as (nameMatch ? 0.6 : 0) + (contentMatch ? 0.4 : 0) multiplied by node.importance.

4.8 Graph Dump (Debug)

const allNodes = graph.dump();
// Returns: array of all node objects — useful for DevTools / debugging

5. Spreading Activation Implementation (In-Browser)

The in-browser spreading activation algorithm in graph.js (and standalone utilities in activation.js) mirrors engram-core/src/activation.rs:

// Within graph.activate(seedId, maxDepth = 3, pruneThreshold = 0.01)
// Returns: Set<string> of activated node IDs

while (queue.length > 0) {
    // Best-first: process highest-strength candidate
    const { id, strength, depth } = bestInQueue();

    if (depth >= maxDepth) continue;

    for (const edgeId of node.edges) {
        const edge = this.edges.get(edgeId);
        const target = this.nodes.get(edge.to);

        // Multiplicative formula
        const targetStrength = strength * edge.weight * Math.max(0, target.importance);

        if (targetStrength <= pruneThreshold) continue;

        // Winner-take-most
        if (targetStrength > prevBest) { /* enqueue */ }
    }
}

Key parameters:

  • maxDepth default: 3 (not 4)
  • pruneThreshold default: 0.01
  • limit default: 20 (applies to spreadActivation() utility; graph.activate() returns a Set with no limit)

Correspondence with engram-core:

  • Same best-first BFS traversal
  • Same multiplicative strength formula
  • Same pruning threshold semantics (0.01)
  • Same winner-take-most rule (strongest path to each node wins)
  • Same depth limit semantics

The in-browser graph omits the cosine_sim factor because embedding vectors are not yet available in the browser.

5.1 Standalone Activation Utilities (activation.js)

import { spreadActivation, activationStrength, reachableNodes, PRUNE_THRESHOLD } from './el-ui.js';

// Multi-seed activation — returns sorted array of reached nodes (excluding seeds)
const reached = spreadActivation(graph, [seedId1, seedId2], { maxDepth: 3, limit: 20 });
// Returns: [{ nodeId, strength, hops, node }]

// Point-to-point strength — returns 0 if no path within maxDepth
const strength = activationStrength(graph, fromId, toId, maxDepth);

// All reachable nodes from a seed (includes seed) — returns Set<string>
const reachable = reachableNodes(graph, seedId, maxDepth);

6. Router — Graph-Based Routing

6.1 Overview

Routes are nodes in the application graph of type 'route'. Navigation activates the target route node. Components subscribed to routing update automatically via spreading activation.

Route nodes start with importance: 0.3. Navigating to a route boosts its importance by 0.2 (capped at 1.0), modeling browser history salience.

6.2 Route Node Connections

The router connects parent routes to child routes via edges (weight: 0.8, relation: 'subroute'). Navigating to /about/team activates the /about route node as well — activation spreads upward through the subroute chain.

6.3 Path Matching

  1. Exact match: /about matches /about
  2. Prefix match (longest wins): /about matches /about/team
  3. Wildcard: '*' catches all unmatched paths

6.4 API

const router = new Router(graph, {
    '/': HomeComponent,
    '/about': AboutComponent,
    '*': NotFoundComponent,
});

router.navigate('/about');
router.navigate('/about', true);   // replaceState instead of pushState
const CurrentPage = router.currentComponent();
const unsub = router.subscribe((path) => console.log('navigated to', path));
const href = router.href('/about');   // returns the path string (identity in v0.1)

The router also listens for popstate events (browser back/forward navigation) and activates the appropriate route node automatically.


7. Compilation Pipeline

7.1 Overview

source.el  →  [Lexer]  →  [Parser]  →  [Codegen]  →  output

The output format depends on the compilation target (see §7.4).

7.2 Lexer

The lexer (src/lexer.rs) is a single-pass O(n) tokenizer. It is context-sensitive: when it encounters the template keyword followed by {, it switches to template mode, producing template-specific tokens:

Token Description
HashIdent(kw) {#if}, {#each}, {#activate} — block open
SlashIdent(kw) {/if}, {/each}, {/activate} — block close
ColonIdent(kw) {:else} — block continuation
OnColon(event) on:click, on:input — event binding
SelfClose />
CloseTag(name) </div>
RawText(s) Inline expression text, text content

Non-template code tokens include keywords (component, props, state, fn, template, if, else, return), identifiers, literals (String, Int, Float, Bool), operators, and punctuation.

7.3 Parser

Hand-written recursive descent parser. Produces Vec<Component>. Each component is parsed as:

  1. component Name { — component declaration
  2. props { ... } — prop definitions (optional)
  3. state { ... } — state definitions (optional)
  4. fn method(...) -> Type { ... } — methods (optional, multiple allowed)
  5. template { ... } — template tree (optional)

The template parses as a tree of TemplateNode values:

Variant Description
Element { tag, attrs, children } HTML element with attributes and children
Component { name, props } Uppercase-initial component reference
Text(s) Literal text content
Interpolation(expr) {expr} — expression interpolation
If { condition, then, else_ } {#if}...{:else}...{/if}
Each { items, item_name, children } {#each items as item}...{/each}
Activate { query, result_name, children } {#activate "query" as name}...{/activate}

Method bodies are captured as raw source text and passed through to the code generator with simple string transformations.

Unknown {#blockname} tags produce a parse error (unknown block tag: #name).

7.4 Code Generator — Multi-Target

The code generator (src/codegen.rs) supports three compilation targets:

CodegenTarget::Web (default) — Legacy JavaScript

Emits an ES2022 JavaScript module using the el-ui JS runtime (graph.js, renderer.js, router.js, index.js). This is the primary target for browser-based applications today.

For each component:

  1. Emits a class extending Component from the runtime.
  2. Constructor: seeds state nodes into this._graph, sets prop values as this._props_{name}, subscribes to activation events.
  3. Emits setState(name, value) which calls this._graph.update() to trigger activation.
  4. Emits render() returning a template literal string.
  5. Translates state assignments: count = count + 1__self.setState('count', count + 1).
  6. Translates template interpolations: {count}${count }.
  7. Emits event handlers as data-el-{event} attributes for the renderer to bind.
  8. Emits ${__self._child(ComponentClass, props)} for component references.

CodegenTarget::Server — Rust SSR

Emits a Rust module where each component is a struct implementing render_to_html(&self) -> String via el_platform::ServerBackend.

Note: The build_node_tree() implementation in this target is a stub — it returns a placeholder PlatformNode::element("div"). Full template-to-PlatformNode codegen is not yet implemented for the server target.

CodegenTarget::Native(Platform) — Rust Native / WASM

Emits a Rust module where each component builds a PlatformNode tree via the semantic primitive layer and mounts it via the el_platform backend for the given platform.

Supported platforms:

Platform Backend Output format
Platform::Web WebBackend (web-sys) Rust compiled to WASM — NOT JavaScript
Platform::Ios IosBackend Rust (objc2-ui-kit)
Platform::Android AndroidBackend Kotlin (Jetpack Compose via build.rs)
Platform::Macos MacosBackend Rust (objc2-app-kit)
Platform::Linux LinuxBackend Rust (gtk4-rs)
Platform::Windows WindowsBackend Rust (windows-rs / WinUI 3)

Note: The build_node_tree() implementation in all native targets is currently a stub returning PlatformNode::element("div"). Full AST-to-semantic-primitive lowering is not yet wired in — the semantic module and per-platform codegen functions exist and are correct, but the compiler does not yet call them from the AST walk.

7.5 Semantic Primitive Layer (semantic.rs)

The semantic layer is the bridge between the template AST and platform-specific code generation. It defines EBD-driven concepts — what something is, not how it looks:

AppearanceConcept — what a control is:

  • Action — primary action trigger (blue/filled on most platforms)
  • Destructive — dangerous/irreversible action (red tint)
  • Secondary — subdued / less-prominent action
  • Navigation — link-like / back-navigation element
  • Informational — read-only display element
  • Structural — layout container with no interactive meaning

LayoutConcept — how children are arranged:

  • Stack — vertical (Column / VStack / GtkBox vertical)
  • Row — horizontal (Row / HStack / GtkBox horizontal)
  • Grid — two-dimensional grid
  • Overlay — z-axis layering (ZStack / GtkOverlay)
  • Scroll — scrollable container

TextConcept — the semantic role of text:

  • Heading { level: u8 } — heading at level 16
  • Body — default body text
  • Caption — small secondary text
  • Label — control label
  • Code — monospaced code text

SemanticPrimitive — the EBD building blocks:

  • Button { label, appearance, on_press } — interactive button
  • Text { content, concept } — text display
  • Container { children, layout } — layout container
  • Input { binding, hint, appearance } — text input bound to state
  • Image { src, alt } — image element
  • Toggle { binding, label } — checkbox/switch bound to boolean state
  • List { items_binding, item_name, item_template } — homogeneous list

Appearance is inferred from explicit appearance="..." attributes, CSS class names, or HTML tag heuristics via infer_appearance(). Layout and text concepts are inferred from tag names via infer_layout_concept() and infer_text_concept().

7.6 CLI

el-ui-compiler App.el             # compiles to App.js (Web target, default)
el-ui-compiler App.el -o app.js   # explicit output path

The CLI always uses CodegenTarget::Web. Target selection (--target server, --target ios, etc.) is available as a CodegenTarget enum in the library API but is not exposed as a CLI flag in the current implementation.


8. Runtime Architecture

8.1 Module Structure

Module Purpose
graph.js Engram graph: nodes, edges, activation, search, subscribe, dump
activation.js Standalone activation utilities: spreadActivation, activationStrength, reachableNodes
renderer.js DOM patching, event binding
router.js Graph-based routing
index.js Component base class, mount(), re-exports

8.2 Component Lifecycle

Lifecycle event Description
constructor(props) Seeds state graph, subscribes to activation
onMount() Called after first render and DOM attachment
render() Returns HTML string; called on activation

Note: onDestroy() is documented in prior drafts but is not implemented in the base Component class or the Renderer. It will not be called by the runtime.

8.3 Mounting

import { mount } from './el-ui.js';
import { App } from './app.js';

const component = mount(App, '#app', { optionalProp: 'value' });
// mount() returns the live Component instance

mount instantiates the root component, creates a Renderer, calls renderer.mount() which calls render(), sets root.innerHTML, binds events, then calls onMount().

8.4 DOM Patching Strategy (v0.1)

The renderer uses full string re-render on every state change: root.innerHTML = component.render(). The activated node set argument to patch() is accepted but unused — targeted patching is planned for v0.2.

After patching, the renderer attempts to restore focus to the previously focused element by id.

Event handlers are rebound after every patch by scanning the new DOM for data-el-{event} attributes. Handlers are compiled via new Function('__self', ...) — this requires a permissive Content Security Policy in v0.1.


9. Platform Backend Architecture (el-platform)

The el-platform crate defines the PlatformBackend trait that all rendering backends implement:

pub trait PlatformBackend: Send + Sync {
    fn name(&self) -> &'static str;
    fn create_element(&self, tag: &str) -> PlatformResult<PlatformNode>;
    fn create_text(&self, content: &str) -> PlatformResult<PlatformNode>;
    fn set_attribute(&self, node: &mut PlatformNode, name: &str, value: &str) -> PlatformResult<()>;
    fn remove_attribute(&self, node: &mut PlatformNode, name: &str) -> PlatformResult<()>;
    fn append_child(&self, parent: &mut PlatformNode, child: PlatformNode) -> PlatformResult<()>;
    fn remove_child(&self, parent: &mut PlatformNode, child_index: usize) -> PlatformResult<()>;
    fn replace_child(&self, parent: &mut PlatformNode, index: usize, new_child: PlatformNode) -> PlatformResult<()>;
    fn bind_event(&self, node: &mut PlatformNode, event: &str, handler: EventHandler) -> PlatformResult<()>;
    fn render_to_string(&self, node: &PlatformNode) -> PlatformResult<String>;
    fn mount(&self, root: PlatformNode, container_id: &str) -> PlatformResult<()>;
    fn patch(&self, old: &PlatformNode, new: &PlatformNode) -> PlatformResult<()>;
    fn supports_ssr(&self) -> bool { false }
}

The ServerBackend is the only backend where supports_ssr() returns true and render_to_string() produces real HTML output. All other backends are platform-native.

Platform is selected via manifest.el:

[platform]
target = "web"   # web | server | ios | android | macos | linux | windows
ssr = true

10. Comparison to Other Frameworks

10.1 vs. React

React uses virtual DOM diffing: on every render, it constructs a virtual tree and diffs it against the previous tree to identify minimal DOM mutations. The unit of re-render is the component; re-render triggers are driven by setState, useReducer, or context changes.

el-ui does not compute a virtual DOM. The spreading activation pass determines the re-render set from the graph topology. State relationships that exist in the graph are automatically propagated; relationships that do not exist are not. There is no need to declare dependencies, use useMemo, or prevent unnecessary renders with React.memo — the graph structure encodes the dependency information directly.

10.2 vs. Vue

Vue uses a Proxy-based reactive system: state values are wrapped in reactive proxies, and reads from these proxies during render are tracked as dependencies. When a reactive value changes, all components that read it during their last render are re-rendered.

el-ui's graph model is explicit: relationships between state nodes are declared via graph edges, not inferred from read tracking. This makes dependencies inspectable and modifiable at runtime. It also enables semantic queries ({#activate}) that find relevant state by meaning rather than by structural reference.

10.3 vs. Svelte

Svelte uses compile-time analysis to determine which parts of the DOM update when which state changes. The compiler emits targeted DOM mutations. There is no runtime overhead for a virtual DOM or a reactive system.

el-ui uses a runtime spreading activation pass. This is more expensive than Svelte's compile-time approach for small, known-static state graphs. The advantage is dynamic state topology: new nodes and edges can be added at runtime without recompilation, enabling state graphs that evolve with program execution.

10.4 Core Differentiator

No prior framework uses Hebbian spreading activation as the re-render decision mechanism. The multiplicative activation formula, the self-seeded activation from a root node, the importance-boost on state change (LTP analog), and the semantic {#activate} query construct have no analogues in any shipping frontend framework as of April 2026.


11. Versioning Roadmap

Version Key changes
v0.1.x Current. Full re-render on state change. String-based {#activate} search. JS (Web) target only via CLI. Native targets available via library API but build_node_tree() is stub.
v0.2.x Targeted DOM patching (only nodes in activation surface). {#activate} with embedding-based semantic search. CLI --target flag for native targets. Full AST→semantic→PlatformNode lowering.
v0.3.x WASM-compiled Engram core replacing JavaScript graph. Sealed artifact support. onDestroy() lifecycle.
v1.0.0 Stable API. Full production sealing. LSP integration with spreading activation autocomplete.