Files
ujsx/tasks/unmount-implementation.md
glm-5.1 c9c32a6aa6 decompose reconciler roadmap into 20 implementation tasks across 5 phases
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).
2026-05-18 16:26:52 +00:00

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
fiber-disposal
reactiveroot-dispose
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() calls disposeFiber(rootFiber, ctx)
  • Root.unmount() calls host.removeChild for top-level children
  • host.finalizeRoot(ctx) called after disposal and removal
  • host.emit("root.unmount", ...) still fires
  • rootFiber reference cleared after unmount
  • Subsequent render() calls after unmount() 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