Files
ujsx/tasks/signal-driven-updates.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

52 lines
2.5 KiB
Markdown

---
id: signal-driven-updates
name: Wire signal changes to prepareUpdate/commitUpdate
status: pending
depends_on: [mount-with-fibers]
created: 2026-05-18T16:22:57.167895441Z
modified: 2026-05-18T16:22:57.167895878Z
scope: moderate
risk: high
impact: phase
level: implementation
---
# Description
Bridge the reactive layer to the host layer by wiring signal changes to `HostConfig.prepareUpdate`/`commitUpdate` via fibers. This is the core of ADR-005: signals handle 90% of updates (property changes), reconciliation handles structural changes.
When a signal that drives a `ReactiveNode` changes, the `computed` recomputes and an `effect` fires. This effect should:
1. Compare the fiber's current props to the new element's props via `host.prepareUpdate`
2. If `prepareUpdate` returns a non-null payload, queue an "update" effect on the fiber
3. Call `host.commitUpdate` with the payload
Multiple signal changes within a batch are collapsed into one reconciliation pass (Preact signals already batch within `batch()` calls). Updates are committed top-down (parent before child) to ensure parent state is consistent when child updates fire.
## Acceptance Criteria
- [ ] Signal change triggers `scheduleUpdate(fiber, nextNode)` on the associated fiber
- [ ] `scheduleUpdate` batches pending updates and queues `flushUpdates` via microtask
- [ ] `flushUpdates` calls `host.prepareUpdate(fiber.instance, fiber.tag, fiber.props, nextNode.props, ctx)`
- [ ] If `prepareUpdate` returns non-null payload, fiber gets `effect: { type: "update", payload }`
- [ ] `commitEffects` calls `host.commitUpdate(fiber.instance, payload, tag, prevProps, nextProps, ctx)`
- [ ] Commit order is top-down (parent before child)
- [ ] Signal effect disposers are stored in `fiber.signalDisposers` for later cleanup
- [ ] `host.prepareUpdate` and `host.commitUpdate` are optional — if not implemented, update is a no-op
- [ ] Existing tests pass
- [ ] New test: signal change on a reactive element triggers `prepareUpdate` + `commitUpdate`
- [ ] New test: batch of signal changes results in single reconciliation pass
## References
- docs/architecture/reconciler.md — Step 1 (Schedule Update), Step 2 (Reconcile Props), Step 4 (Commit Effects)
- docs/architecture/decisions/005-signal-driven-updates-over-tree-diffing.md — ADR-005
- docs/architecture/reactive-layer.md — ReactiveNode, ReactiveRoot, signal/computed/effect
- docs/architecture/host-config.md — prepareUpdate, commitUpdate methods
## Notes
> To be filled by implementation agent
## Summary
> To be filled on completion