LearnNewsExamplesServices
Frontmatter
id17579
titlelist.Buffered never receives its initial viewport size
stateClosed
labels
bugaitestingregressionperformancecore
assigneesneo-gpt-emmy
createdAtAug 22, 2026, 11:50 PM
updatedAtAug 23, 2026, 2:49 AM
githubUrlhttps://github.com/neomjs/neo/issues/17579
authorneo-gpt-emmy
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 17550 Activity stream: scrollable buffered list, honest counts, per-row local times
closedAtAug 23, 2026, 2:49 AM

list.Buffered never receives its initial viewport size

Closed Backlog/active-chunk-18 bugaitestingregressionperformancecore
neo-gpt-emmy
neo-gpt-emmy commented on Aug 22, 2026, 11:50 PM

Context

PR #17557 resolved #17554 by landing Neo.list.Buffered. Its first real product adoption is #17550: the Fleet activity history binds 500 producer-owned records into a fixed-height pooled list.

The live adoption exposed a post-merge engine defect before #17550 could encode its headed witness. On this branch at 2b281a7639 plus the #17550 working delta, the activity list's DOM viewport measured 139.10px with itemHeight: 52 (three visible rows), while App-Worker truth remained viewportHeight: 52, availableRows: 1, and a nine-row pool. Calling addResizeObserver(true) again did not change that state.

The Problem

Neo.list.Buffered never receives its initial real viewport size in the live app. Its pool stays sized from the one-row fail-visible fallback, so a taller seat under-mounts the intended viewport-plus-buffer range. A sufficiently tall pane can expose blank or late-rebound history during scroll even though Store truth is complete.

The registration payload explains the miss:

  • src/list/Buffered.mjs:156-160 calls the main-thread addon with {id, windowId}.
  • src/main/addon/ResizeObserver.mjs:361-376 records data.componentId as the App-Worker delivery target.
  • src/manager/DomEvent.mjs:121-132 is the working contract: {componentId: component.id, id: targetId, windowId}.

Without componentId, native size delivery has no component recipient.

The Architectural Reality

The bug belongs to Neo.list.Buffered, not its first consumer. The list owns viewportHeight, availableRows, pool cardinality, and its ResizeObserver lifecycle. #17550 must consume that contract; adding app-local DOM measurement or a second observer would duplicate the engine primitive the ticket explicitly waited for.

This is a narrow repair to an existing public engine method. No new module or directory choice is introduced.

The Fix

  1. Send {componentId: id, id, windowId} from Buffered#addResizeObserver() on register and unregister.
  2. Strengthen the unit receipt from “addon called” to the exact routing envelope.
  3. Add a mounted consumer witness: a viewport spanning three fixed rows updates availableRows to three and mounts viewport + both buffer sides over a 500-record Store.
  4. Re-run #17550's live activity seat and preserve its existing prepend-anchor and bounded-pool contracts.

Contract Ledger

Target surface Source of authority Proposed behavior Fallback Docs Evidence
Neo.list.Buffered#addResizeObserver(mounted) ResizeObserver#register + DomEvent#addResizeObserver register/unregister names both DOM target id and App recipient componentId invalid/missing DOM target keeps the addon's bounded retry behavior JSDoc clarifies routing identity exact-envelope unit + live mounted-size receipt
availableRows / pool cardinality Buffered#onResize real viewport height drives ceil(height/itemHeight); pool stays bounded to viewport + 2 * bufferRowRange one-row fallback only before first real delivery existing class JSDoc remains authority 500-record consumer witness

Decision Record impact

None — restores the shipped fixed-height windowing contract.

Acceptance Criteria

  • ResizeObserver register and unregister payloads include componentId: list.id, id: list.id, and the exact windowId.
  • A 139px viewport with itemHeight: 52 yields availableRows: 3; a 500-record Store mounts exactly three visible rows plus the configured leading/trailing buffer, never 500 components.
  • Runtime resize changes pool cardinality through the canonical addon delivery; excess pooled components retire through the existing trim path.
  • Existing scroll, prepend-anchor, selection, focus, and record-change tests remain green.
  • #17550's headed activity witness confirms Store count, mounted-row bound, and nonzero real viewport size on both themes.

Out of Scope

  • A repo-wide rewrite of other ResizeObserver consumers.
  • Variable-height list rows.
  • Fixed-DOM-order recycling beyond the current no-insert/remove guarantee; #17563 owns that follow-up.
  • Any app-local observer or manual DOM measurement workaround.

Avoided Traps

  • Hard-code the activity pane height. Works at one dock split and breaks on resize/tear-out.
  • Measure from the app. Creates a second geometry authority beside the engine list.
  • Reopen #17554. It was resolved by PR #17557; this is a new post-merge defect and therefore a new successor.

Related

#17550 · #17554 · #17563 · PR #17557 · historical external-target routing repair #9316.

Live latest-open sweep: checked latest 20 open issues at 2026-08-22T21:50:45.051Z; no equivalent found. A2A in-flight sweep: checked latest 30 all-state messages at 2026-08-22T21:50:45.051Z; no overlapping claim.

Origin Session ID: f47f948b-743b-4c11-84a8-fa60a567a148

Retrieval Hint: query_raw_memories("list.Buffered ResizeObserver componentId viewportHeight availableRows activity 500")

🪡 Emmy (GPT-5.6 Sol Ultra, Codex)

tobiu referenced in commit 68505df - "feat(agentos): buffer the Fleet activity history (#17550) (#17585) on Aug 23, 2026, 2:49 AM
tobiu closed this issue on Aug 23, 2026, 2:49 AM