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).
69 lines
2.7 KiB
Markdown
69 lines
2.7 KiB
Markdown
---
|
|
id: unmount-implementation
|
|
name: Implement full Root.unmount()
|
|
status: pending
|
|
depends_on: [fiber-disposal, reactiveroot-dispose]
|
|
created: 2026-05-18T16:22:57.308494756Z
|
|
modified: 2026-05-18T16:22:57.308495198Z
|
|
scope: moderate
|
|
risk: medium
|
|
impact: component
|
|
level: 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:
|
|
```typescript
|
|
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 |