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.5 KiB
id, name, status, depends_on, created, modified, scope, risk, impact, level
| id | name | status | depends_on | created | modified | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|---|---|
| reactiveroot-dispose | Implement ReactiveRoot.dispose() | pending |
|
2026-05-18T16:22:57.292895316Z | 2026-05-18T16:22:57.292895758Z | narrow | medium | component | 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():
- Calls the render effect disposer and clears it
- Iterates all tracked subscriber disposers and calls each one
- 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 implementeddispose()calls render effect disposer and clearsrenderDisposerdispose()iterates all tracked subscriber disposers and calls eachdispose()clears internal tracking state after runningsubscribe()tracks disposer returns in an internalSet<() => void>subscribe()returned unsubscribe function calls disposer AND removes from trackingdispose()and unsubscribe functions are idempotent (safe to call twice)reactiveComponentandreactiveElementreturn real dispose functions (not no-ops)ReactiveNode.disposecleans 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