LearnNewsExamplesServices
Frontmatter
id17536
titleA cleared grid pool row retains worker-side record identity
stateClosed
labels
bugaitestinggrid
assignees[]
createdAtAug 22, 2026, 2:48 AM
updatedAtAug 22, 2026, 2:53 PM
githubUrlhttps://github.com/neomjs/neo/issues/17536
authorneo-gpt-emmy
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 22, 2026, 2:53 PM

A cleared grid pool row retains worker-side record identity

Closed Backlog/active-chunk-18 bugaitestinggrid
neo-gpt-emmy
neo-gpt-emmy commented on Aug 22, 2026, 2:48 AM

Context

PR #17523 surfaced a useful invariant while attempting to resolve #17427: a pooled row whose worker-side record is null should not retain the prior record's identity in its worker-side VDOM.

That is smaller than #17427. Both the recordId deletion and display: none are staged under silent: true and depend on the same Body#createViewData trailing update to reach the browser. If that flush is lost, neither mutation changes the painted DOM. This ticket therefore owns only the worker-state invariant; #17427 retains the flush/delivery defect.

Live latest-open sweep: checked the latest 20 open issues and the 30 most recent all-state A2A messages at 2026-08-22T00:48:39.433Z; no equivalent narrow ticket or in-flight claim was found. #17427 is related but intentionally owns the painted-DOM flush boundary.

The Problem

Row#createVdom clears a pool slot by setting record: null, but the pre-fix VDOM retains data.recordId from the record that previously occupied that slot. Worker-side inspection can therefore observe contradictory truth: row.record === null while row.vdom.data.recordId still names a record.

That mismatch is independently wrong even when the eventual browser update hides the row. It misleads worker-plane diagnostics and makes later reasoning about painted versus staged state harder.

It is not, by itself, the cause or repair for a painted duplicate when the trailing update is lost. A silent VDOM mutation has no independent browser delivery path.

The Architectural Reality

  • src/grid/Body.mjs:createViewData clears unused pool slots through item.updateContent({record: null, rowIndex: -1, silent: true}) and performs one trailing update() when the outer call is not silent.
  • src/grid/Row.mjs:createVdom mutates the worker-side VDOM. Under silent: true, those mutations are staged; they do not directly update the physical DOM.
  • data.recordId identifies the record claim. data.rowId identifies the reusable pool slot and remains valid after the record is cleared.

The Fix

When Row#createVdom sees no record, remove only vdom.data.recordId, retain vdom.data.rowId, stage display: none, and preserve the existing update/batching behavior.

The permanent test must assert worker-side VDOM truth and its inverse control. It must not describe that assertion as painted-DOM delivery.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
Row#createVdom clear branch src/grid/Row.mjs record === null implies no worker-side vdom.data.recordId Retain the data object and rowId; do not force an extra update Intent comment at the branch RED/GREEN pooled-row unit arm
Live pooled row identity Body#getRecordId + Row#createVdom A row holding a record retains the matching recordId None Test control explains inverse Positive control arm

Decision Record impact: none — local grid pooling invariant; no API or ADR authority changes.

Acceptance Criteria

  • The pre-fix tree is RED when a cleared pool row retains vdom.data.recordId; the fixed head is GREEN.
  • Every pooled row with record === null has no worker-side vdom.data.recordId.
  • Every pooled row that still holds a record retains its matching recordId.
  • rowId and the vdom.data object remain intact on the clear path.
  • Existing pooling/teleportation delta-count contracts remain unchanged.
  • Source, test, commit, and PR prose explicitly distinguish staged worker VDOM from painted DOM.

Out of Scope

  • Making the silent clear reach the DOM when the trailing body update collides or is lost.
  • Resolving #17427's flush-bound derivation, AC-4, or AC-5.
  • Changing row pooling, slot assignment, or delta depth.

Avoided Traps

Calling worker VDOM painted truth. A silent mutation is not a browser update; the outer flush remains the delivery boundary.

Deleting the whole data object. rowId still identifies the pool slot, and removing it changes the guarded delta shape.

Closing the broader flush ticket with a staged-state invariant. #17427 remains the authority for the unresolved painted-DOM defect.

Related

  • #17427 — retains the lost/colliding flush and painted-DOM correctness problem.
  • PR #17523 — current implementation candidate; should retarget to this ticket and correct its delivery claims.
  • PR #17458 — prior invariant guards that did not reproduce #17427.

Origin Session ID: bbd4f722-ca03-4269-a88e-29555b12b9f9

Retrieval Hint: grid Row createVdom cleared pool worker vdom recordId silent flush PR 17523

tobiu referenced in commit f57af55 - "fix(grid): a cleared pool row stops claiming its record in worker-side VDOM (#17536) (#17523) on Aug 22, 2026, 2:53 PM
tobiu closed this issue on Aug 22, 2026, 2:53 PM