bug(runtime): scope in-flight updates to the owning view epoch
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 35
- Forks
- 15
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 103
Description
Summary
A delayed update commit from an old view epoch can block updates in a fully mounted replacement view. When the old acknowledgement eventually arrives, it drops the new epoch's queued update instead of allowing it to run.
Reproduced on public main 8f2eba12c2c980d0f4d92713106e798891738106, Node 22.22.1, pnpm 10.32.1, Vitest 2.1.9 / happy-dom, Windows. This is a RuntimeSession deterministic host test, not a claim of a reproduced browser race in every Adapter.
Authority and ownership
- C-LIFECYCLE-0006-C/D (draft): old-epoch bindings/completions become invalid; a remount retains the logical instance.
- C-LIFECYCLE-0008-D/E (active): superseded completions become inert; view detach preserves instance-owned logical resources.
- session.ts owns update scheduling.
updateInFlight/updateQueuedare instance-wide booleans (lines 72–73).startUpdategates on them; completion clears them before/within the stale-epoch branch (lines 225–241).unmountInternalinvalidates lifecycle work without releasing that update ownership. - Existing transition tests check a stale update acknowledgement before the next mount completes. They do not issue a new update after remount while that old acknowledgement is still pending.
Deterministic event trace
Use createControlledHost from the linked test (capture CommitSignal values and scheduled tasks; do not auto-complete them):
- Mount epoch 1; explicitly call its commit
done()and scheduled mounted task. - Call
session.controller.update(); retain its commit signal without completing it. - Await
session.unmount(). Mount epoch 2; explicitly complete its mount commit and mounted task. - Call
session.controller.update()in the now-mounted epoch 2. - Observe no render and no new commit signal.
- Complete epoch 1's retained update signal.
- Still observe no epoch-2 update. A third explicit update finally works.
Observed render counts: 1 after first mount, 2 after epoch-1 update render, 3 after remount, still 3 after steps 4 and 6, then 4 only after the third update. Same-epoch update coalescing drains correctly in the paired control.
The old acknowledgement may arrive eventually: the failure does not depend on assuming a permanently broken host. If it never arrives after its view is removed, the replacement epoch stays blocked.
Proposed bounded direction
Make update ownership explicit, e.g. an epoch/request-identity record. Detach invalidates the old view's in-flight update; a stale completion may settle its own work but cannot clear, block or consume a newer epoch's update. Preserve logical state and same-epoch coalescing. Do not add sleeps, retries, debounce windows or a third update to a consumer.
Acceptance and scope
- The exact sequence above commits epoch 2's update without depending on epoch 1's acknowledgement.
- Test old acknowledgement before/during/after the replacement commit; no stale effects/callbacks, lost intent, or overlapping current commits.
- Keep same-epoch coalescing, detached prop synchronization, terminal disposal and callback failure behavior.
- Add Runtime coverage alongside the existing transition tests, then exercise at least one deferred-commit Adapter; record untested adapters explicitly.
- Reconcile applicable T-LIFECYCLE mappings if extending their coverage.
This report authorizes no claim or implementation by itself. It proposes a bounded repair of existing lifecycle behavior, not a new scheduler API or lifecycle promotion. #645 concerns Boundary/Overlay stack priority, and #623 concerns Scroll follow ownership; neither is this Runtime update-liveness defect.
Evidence: the new observation and its normal coalescing control both reproduced deterministically without sleeps. Existing selected lifecycle/state/text-control/host-lease suites also pass, exposing a missing cross-epoch test case. AI-assisted audit requested by the current user; no product changes or human acceptance claimed.
Executed evidence (backfilled 2026-09-17)

Rendered test transcript from the fresh Node 22.22.1 run described in the reproduction above: the observation tests assert the existing failure and paired controls, not a repair. Image pinned to evidence-only commit da071c9e on branch codex/issue-visual-evidence-20260915 (never to be merged into main).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with packages/runtime/src/instance/session.ts, especially updateInFlight/updateQueued, startUpdate, and unmountInternal. Then read the existing lifecycle transition tests and their createControlledHost setup, and run the deterministic sequence with deferred commit signals. Done means epoch-2 updates commit despite the old acknowledgement, while coalescing, stale completion handling, disposal, callbacks, and Adapter coverage remain correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100