# SKILL: Runtime — internals

Tier 2. Para uso normal alcanza AIREF.md.

## Proxy auto-unwrap (`renderer.ts`)

`mountComponent` envuelve `state`/`computed` en Proxy:
```typescript
new Proxy(obj, { get(t,p) { const v=t[p]; return isSignal(v) ? v.value : v } })
```
Consecuencia silenciosa: `state.x.value` en template = `bool.value` = `undefined`. NO error. Render vacío. (En script SÍ usás `.value`.)

## Snapshot pattern — closure en computed

```typescript
// ❌ dep no se registra
const cls = computed(() => (tab) => tab === activeTab.value ? 'on' : 'off')
// ✅ leer dep en el getter
const cls = computed(() => { const a = activeTab.value; return (t) => t === a ? 'on' : 'off' })
```

## onMount cleanup — return function corre al unmount (v0.24.6+)

```typescript
onMount(() => {
  const id = setInterval(() => { tick++ }, 1000)
  return () => clearInterval(id)   // ← se ejecuta al unmount del componente
})
```

**Antes de v0.24.6 el return se ignoraba silenciosamente** — timers/listeners declarados así nunca se limpiaban. Si tu proyecto usaba este patrón en versiones previas, los intervals quedaban corriendo en background y la memoria crecía. Fixed con regresión test E2E.

## Lifecycle — estado global del módulo

`currentMountHooks`/`currentErrorHooks` viven a nivel módulo. Solo válidos SINCRÓNICAMENTE en `setup()`. NO en async/setTimeout callbacks.

## onError — scope chain

`_currentParentScope` (module-level). `dispatchError(err, scope)` camina `scope.parent` hasta encontrar handler que retorne `true`.

- Captura: setup, render, onMount/onUpdate, errores de hijos.
- NO captura: onUnmount, unhandled rejections, async handlers fuera de scope sync.
- Handler async → loggea warning (Promise ≠ boolean → no suprime).
- Sin handler que suprima → `<span data-ark-error>` + `[arckode-ui] render error in <Comp>:`. App sigue.

## Keyed reconciliation (v0.16+)

Si TODOS los hijos no-texto tienen `:key`:
1. `oldByKey: Map<key, {node, vnode}>`
2. Walk new: key existe → reusa + patch + mueve; sino → crea.
3. Remueve viejos no usados.

Mezclar keyed + unkeyed → fallback posicional. `:key` se filtra en `applyProps` (no DOM).

## SVG — case-sensitive

Set explícito (`foreignObject`, `clipPath`, `feGaussianBlur`...) + fallback lowercase. `class` con `setAttribute('class', ...)` (`.className` es read-only en `SVGElement`).

## Emit hijo→padre — sin prefijo

`emit(name, payload)` → `dispatchEvent(new CustomEvent(name, { detail: payload }))`. Wrapper `<div style="display:contents">` registra props `onX` como listeners. Handler del padre recibe `e.detail` directo.

Anti-pattern viejo `ark:event` → rompía `@event`. NO volver.

## Tests

```typescript
// @vitest-environment happy-dom
function makeContainer() { const d = document.createElement('div'); d.id='app'; document.body.appendChild(d); return d }
```
`effect()` síncrono. `watch()` async (`queueMicrotask`) → esperar microtask antes de assertear.
