Skip to content

Lifecycle ownership bugs

Where

Widget tree, graph views, signal callbacks throughout assets/lua/

Problem

Before the April 2026 lifecycle hardening, the runtime had no clear ownership model:

  • Frame update was split across multiple disconnected callers, each updating partially overlapping subsets
  • Layout and widget teardown were confused — dropping layout didn't drop widgets, leaving orphaned objects
  • Async completions could fire after the owning object was dropped (use-after-drop)
  • Post-drop state was undefined — no explicit contract for what remains valid after teardown
  • Stale rebuild contexts: when a widget was rebuilt, the rebuild function captured stale references to the old state

Why it matters

These bugs were the single largest source of runtime crashes during development. Without clear ownership, every async callback, every signal handler, and every rebuild was a potential crash site.

Plan

Resolved in April 2026 through a series of hardening commits:

  1. fix(ui): centralize frame update ownership — single update entry point owns the full update pass
  2. fix(ui): harden signal and graph view lifecycle — signals disconnect when owners drop
  3. fix(ui): stop double-dropping composite view children — teardown ownership clarified
  4. See Lifecycle Invariants for the finalized rules
  5. See Lifecycle Hardening Plan for the full strategy