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).
52 lines
2.5 KiB
Markdown
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 |