Proto-UI / Proto-UI/Proto-UI

bug(runtime): scope in-flight updates to the owning view epoch

Open
#647 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: core bug
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/updateQueued are instance-wide booleans (lines 72–73). startUpdate gates on them; completion clears them before/within the stale-epoch branch (lines 225–241). unmountInternal invalidates 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):

  1. Mount epoch 1; explicitly call its commit done() and scheduled mounted task.
  2. Call session.controller.update(); retain its commit signal without completing it.
  3. Await session.unmount(). Mount epoch 2; explicitly complete its mount commit and mounted task.
  4. Call session.controller.update() in the now-mounted epoch 2.
  5. Observe no render and no new commit signal.
  6. Complete epoch 1's retained update signal.
  7. 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)

Executed trace and explanatory diagram for #647

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.