Tasks follow the architecture spec phases: - Phase 0: key field on UElement (2 tasks + review) - Phase 1: reactive-host bridge / fiber tree (4 tasks + review) - Phase 2: key-based children reconciliation (3 tasks + review) - Phase 3: unmount & dispose support (4 tasks) - Phase 4: TypeBox value optimizations (4 tasks) Validated with taskgraph CLI: no cycles, 15 parallel generations, 3 high-risk tasks identified (signal-driven-updates, commit-mutations, fiber-disposal).
2.7 KiB
id, name, status, depends_on, created, modified, scope, risk, impact, level
| id | name | status | depends_on | created | modified | scope | risk | impact | level | ||
|---|---|---|---|---|---|---|---|---|---|---|---|
| unmount-implementation | Implement full Root.unmount() | pending |
|
2026-05-18T16:22:57.308494756Z | 2026-05-18T16:22:57.308495198Z | moderate | medium | component | implementation |
Description
Replace the stub Root.unmount() with a full implementation that tears down the fiber tree, removes host instances, disposes signal subscriptions, and calls host cleanup methods.
Current stub:
unmount() {
host.finalizeRoot?.(ctx);
host.emit?.("root.unmount", `root_${Date.now()}`, {});
}
New flow:
root.unmount()
→ disposeFiber(rootFiber, ctx) // disposes signals, calls finalizeInstance bottom-up
→ removeChild for each top-level child // host removal (top-down from root, each child subtree already disposed)
→ host.finalizeRoot(ctx) // root-level host cleanup
→ emit "root.unmount"
→ clear rootFiber reference
Note: disposeFiber handles resource cleanup (signals, finalizeInstance) but NOT host.removeChild. We need to walk the fiber tree and call removeChild for host instance removal. However, since children are already disposed, we only need to remove top-level children from the root context.
Open question from lifecycle.md: Should ReactiveRoot auto-dispose on root.unmount()? The architecture doc suggests decoupling is safer (a ReactiveRoot could drive multiple roots). This task should keep them decoupled — the consumer is responsible for calling ReactiveRoot.dispose() separately if needed.
Acceptance Criteria
Root.unmount()callsdisposeFiber(rootFiber, ctx)Root.unmount()callshost.removeChildfor top-level childrenhost.finalizeRoot(ctx)called after disposal and removalhost.emit("root.unmount", ...)still firesrootFiberreference cleared after unmount- Subsequent
render()calls afterunmount()start fresh (new mount) - Signal subscriptions from the fiber tree are cleaned up
- Does NOT auto-dispose
ReactiveRoot— consumer responsibility unmount()is idempotent (safe to call twice)- Existing tests pass
- New test: unmount cleans up all host instances
- New test: unmount then re-render works (fresh mount)
- New test: double unmount is safe
- New test: unmount calls finalizeInstance for each fiber
References
- docs/architecture/lifecycle.md — Root Unmount Flow, Host Notification
- docs/architecture/host-config.md — Known Gaps: unmount is a stub
- docs/architecture/lifecycle.md — Open Question 2 (ReactiveRoot auto-dispose on unmount)
Notes
To be filled by implementation agent
Summary
To be filled on completion