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).
This commit is contained in:
57
tasks/reactiveroot-dispose.md
Normal file
57
tasks/reactiveroot-dispose.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: reactiveroot-dispose
|
||||
name: Implement ReactiveRoot.dispose()
|
||||
status: pending
|
||||
depends_on: [review-reconciler]
|
||||
created: 2026-05-18T16:22:57.292895316Z
|
||||
modified: 2026-05-18T16:22:57.292895758Z
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
# Description
|
||||
|
||||
Implement real disposal in `ReactiveRoot` to close the signal subscription leak gap. Currently, `ReactiveRoot` has no `dispose()` method, `subscribe()` return values are not tracked, and `render()` overwrites the previous disposer without disposing it.
|
||||
|
||||
`ReactiveRoot.dispose()`:
|
||||
1. Calls the render effect disposer and clears it
|
||||
2. Iterates all tracked subscriber disposers and calls each one
|
||||
3. Clears internal tracking state
|
||||
|
||||
`subscribe()` is updated to track disposer returns: each `effect()` created by `subscribe()` has its disposer stored in an internal list. The returned unsubscribe function both calls the disposer and removes it from tracking.
|
||||
|
||||
Both `dispose()` and the returned unsubscribe functions are idempotent.
|
||||
|
||||
Also implement real `dispose` on `ReactiveNode` — currently both `reactiveComponent` and `reactiveElement` return `dispose: () => {}`. With proper disposal tracking, these should return actual cleanup functions.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `ReactiveRoot.dispose()` method implemented
|
||||
- [ ] `dispose()` calls render effect disposer and clears `renderDisposer`
|
||||
- [ ] `dispose()` iterates all tracked subscriber disposers and calls each
|
||||
- [ ] `dispose()` clears internal tracking state after running
|
||||
- [ ] `subscribe()` tracks disposer returns in an internal `Set<() => void>`
|
||||
- [ ] `subscribe()` returned unsubscribe function calls disposer AND removes from tracking
|
||||
- [ ] `dispose()` and unsubscribe functions are idempotent (safe to call twice)
|
||||
- [ ] `reactiveComponent` and `reactiveElement` return real dispose functions (not no-ops)
|
||||
- [ ] `ReactiveNode.dispose` cleans up the underlying computed subscription
|
||||
- [ ] Existing tests pass
|
||||
- [ ] New test: `ReactiveRoot.dispose()` prevents future effect fires
|
||||
- [ ] New test: `subscribe()` unsubscribe removes from tracking
|
||||
- [ ] New test: double `dispose()` is safe
|
||||
- [ ] New test: `reactiveComponent.dispose()` prevents future computed evaluations
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/reactive-layer.md — Known Gaps: all dispose functions are no-ops, subscribe return value thrown away, render overwrites previous disposer, no auto-dispose on unmount
|
||||
- docs/architecture/lifecycle.md — ReactiveRoot Disposal section
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in New Issue
Block a user