Skip to content

feat: extract ChatPage transcript scroll shell behind a characterization net - #7977

Merged
bolichen97 merged 1 commit into
mainfrom
test/chatpage-scroll-shell-pins
Sep 3, 2026
Merged

feat: extract ChatPage transcript scroll shell behind a characterization net#7977
bolichen97 merged 1 commit into
mainfrom
test/chatpage-scroll-shell-pins

Conversation

@CrysisDeu

@CrysisDeu CrysisDeu commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Why no screenshot: behavior-preserving extraction — JSX moves verbatim behind a component seam with byte-identical DOM; the mounted-DOM render suite pins child order and the golden-frame harness (follow-up baseline) is the visual evidence mechanism itself.

What

Extracts ChatPage's transcript scroll shell — the scroll container's full style contract, the virtualizer's sentinel/spacer wiring, and the older-messages loading affordance — into website/src/pages/chat/TranscriptScrollShell.tsx. Page content threads through three slots (aboveRows / children / belowRows), so page state never leaks into the shell and any host running useVirtualChat can reuse it. Behavior-preserving by design: the extracted region renders byte-identical DOM.

Follow-up to #7567 (shared scroll chrome); this is phase 2 of the chat-core render unification.

The characterization net (written FIRST, then the extraction landed under it)

Layer File What it pins
Source-token recipe (23 pins) website/src/test/ChatPage.scrollShell.recipe.test.tsx every load-bearing token of the shell, sliced to the owning element (onScroll on the scroller, spinner class on the spinner)
Mounted-DOM render suite website/src/test/ChatPage.scrollShell.render.test.tsx scroller child order incl. slots, spinner mount condition, a dispatched scroll event reaching the host callback, scrollerStyle override resistance (spread-first), slot MEMBERSHIP with negative assertions on the children region
Mutation harness website/scripts/mutation-check-scroll-shell.mjs (npm run mutation:scroll-shell) line-deletion mutants across 83 targets incl. the prop-plumbing seam and the shell import; verdicts from vitest's JSON report; caught split reported as tests / parse / collection so nothing launders the pin score; dead-exclusion detection; crash-safe restore
Golden frames website/scripts/capture-chatpage-scroll-shell.mjs (npm run golden:scroll-shell) + website/capture/chatpage-scroll-shell.tsx 8 self-asserting themed frames (bottom-pinned / jump-pill / short / paging with the earlier-messages bar AND sticky spinner on screen); per-frame theme-applied + fixture-parity checks; dark≠light byte-difference derived from the frame manifest; capture promotes by directory swap only when every check passes; --compare enforces the exact manifest and refuses identical dirs

Last full local run before test execution moved to CI: mutation 53/53 caught 0 survivors (pre-expansion; targets now 83), 55 files / 525 tests green, tsc -b and eslint clean, goldens byte-deterministic across consecutive captures.

Adversarial review

4 rounds × 4 cross-vendor blind reviewers (claude-opus-5 / gpt-5.6-sol / deepseek-3.2 / qwen3-coder-next), fix-and-re-review loop per round. Round-4 verdict: the extraction itself reads clean (both blocking-grade reviewers); every remaining actionable was a net-quality item and was fixed (spinner-class pin de-vacuated, prop-plumbing seam + import added to mutation scope, collection-failure verdicts split out, capture-script drift + non-atomic promotion fixed). Notable catches across rounds: counterfeit dark-theme goldens (ThemeProvider overrode the manual data-theme), a paging scene missing from the visual net, and an EdgeFade reparenting regression introduced (and reverted byte-identically) during the loop.

Deferred to follow-up (deliberate)

  • Golden baseline PNGs are NOT committed here: the local baseline predates the final tree state. Capture on this head via npm run golden:scroll-shell and commit in a follow-up (harness + npm entry ship in this PR).
  • A mounted parentage assertion for the header EdgeFade (offsetParent = title row).
  • SIGTERM/SIGHUP restore handlers in the mutation harness (SIGINT + finally are handled).
  • Wiring mutation:scroll-shell / golden:scroll-shell into CI workflows.
  • Generalizing the shell's i18n keys / chat-container class when a second consumer adopts it (YAGNI until then).

No visual delta intended: this PR moves JSX verbatim behind a component seam; the render suite and (follow-up) goldens are the evidence.

no linked issue: phase-2 refactor tracked in the chat-core RFC, not a GitHub issue.

@CrysisDeu
CrysisDeu requested a review from a team September 2, 2026 19:31
@CrysisDeu
CrysisDeu requested a review from a team as a code owner September 2, 2026 19:31
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — ✅ PASS

UX-level review of 5cdb25e94afe365942392e6572ecb59e5d1809de — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All evidence confirms a behavior-preserving refactor: the JSX moves verbatim (same strings, same i18n keys, same ARIA, same child order, same scroll-contract styles with paddingBottom: 16 re-supplied via scrollerStyle spread-first so the shell's tokens win), and everything else in the diff is test/tooling infrastructure with no user-facing surface. No screenshots to audit, no committed images.

UX-Verdict: PASS

Byte-identical JSX extraction — every string, ARIA attribute, child order, and scroll behavior is preserved verbatim; no user-facing surface changes.

[UX-REVIEWED] 5cdb25e

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — 🟡 CONCERNS

Design-level review of 5cdb25e94afe365942392e6572ecb59e5d1809de — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

I have enough to render the verdict. The extraction is a faithful verbatim move (slot order matches the original DOM sequence, spread-first style merge preserves page geometry), the repo already has precedent for source-token pinning (fadeClearance) and capture scripts, and the feature map was updated. The notable design risk is that two of the four advertised net layers ship inert: the mutation harness's expanded 83-target scope was never run to completion anywhere (the description's last full run is "53/53, pre-expansion"), golden baseline PNGs aren't committed, and neither script is wired into CI — all deferred to follow-ups.

Design-Verdict: CONCERNS

Sound, faithful extraction — but half its advertised safety net ships inert, unproven on this head, and dependent on follow-ups to exist at all.

Watch

  • The verification story outruns the code: goldens have no committed baseline ("Golden baseline PNGs are NOT committed here"), the mutation harness has never completed a run at its shipped scope ("mutation 53/53 caught 0 survivors (pre-expansion; targets now 83)"), and neither is in CI. Until the follow-ups land, regressions in the shell region are guarded only by the two vitest suites, while ~460 lines of bespoke harness sit as dead scripts that rot silently if the follow-ups slip.
  • The recipe suite pins page-specific tokens (pages.chatPage.* keys, chat-container) inside a component whose stated purpose is reuse by "any host running useVirtualChat" — the first real second consumer forces a net rewrite the suite's own header discourages.

Suggestions

  • Run the 83-target mutation sweep and capture/commit the golden baseline on this head (or fold the CI wiring in here), so the net is live the moment it merges rather than contingent on follow-ups.

[DESIGN-REVIEWED] 5cdb25e

Comment thread website/scripts/mutation-check-scroll-shell.mjs Fixed
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of 5cdb25e94afe365942392e6572ecb59e5d1809de — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All evidence is gathered. Final review:

First-Principles-Verdict: CONCERNS

The extraction is well-derived phase-2 work, but its two heaviest verification layers ship unexecuted in their final form, and the reuse API has one consumer.

What this change ships

Intent: make the upcoming chat-core scroll-render unification safe by moving ChatPage's transcript scroller skeleton behind a component seam pinned by a characterization net — an ADDITION (refactor plus tooling); description matches the diff.

  1. Transcript scroller now lives in TranscriptScrollShell.tsx, content threaded via slots — justified (follow-up to shipped ChatScrollChrome).
  2. Reusable shell API: scrollerStyle prop + exported TranscriptVirtWiring — one consumer, generalized.
  3. Shell style tokens now beat host overrides (spread-first) — declared, new behavior the inline code never had.
  4. Source-token recipe suite (23 pins) — justified; reuses the existing fadeClearance technique.
  5. Mounted-DOM render suite pinning child order and slot membership — justified.
  6. mutation:scroll-shell npm script + 256-line bespoke harness, first of its kind — declared; final form never run.
  7. golden:scroll-shell npm script + capture script/entry — declared; ships no baseline, no CI wiring.
  8. Feature-map doc row — mandated by docs rules.

Watch

  • Evidence gap: "Last full local run… mutation 53/53 caught 0 survivors (pre-expansion; targets now 83)" — the shipped 83-target harness has never completed a run, so 30 targets are unproven; neither new script is in CI (both deferred). ~565 lines of net tooling currently verify nothing on this head.
  • "any host running useVirtualChat can reuse it": production hosts counted — 2 (ChatPage.tsx:6177, ArtifactsPage.tsx:547), and ArtifactsPage is an artifacts grid that cannot take a chat shell (h-16 header spacer, older-messages spinner, chat-container class). The second consumer is prospective, not counted.

Subtractions

  • Drop export on TranscriptVirtWiring — grep counts 2 hits, both inside its own file; zero importers.
  • Shrink scrollerStyle?: React.CSSProperties — one caller ever, always { paddingBottom: 16 } (lens-5 same-value parameter); inlining the padding (or a plain number prop) also deletes the override hazard that the spread-first ordering and its dedicated render test exist to defend.
  • Defer capture-chatpage-scroll-shell.mjs + capture/chatpage-scroll-shell.{html,tsx} + the npm entry to the follow-up that commits the baseline PNGs — until baselines exist the golden layer compares nothing, and landing harness and baseline together keeps them in lockstep.

[FIRST-PRINCIPLES-REVIEWED] 5cdb25e

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 5cdb25e94afe365942392e6572ecb59e5d1809de — this comment is updated in place on each push.

Review details

This is a pure component-extraction refactor with an extensive characterization-test net. I independently verified the parity claim from the diff.

Child order in the new TranscriptScrollShell (TranscriptScrollShell.tsx:867-943): header spacer → {aboveRows} (gated EarlierMessagesBar) → top sentinel → loadingOlder spinner → offsetBefore spacer → {children} (message items) → offsetAfter spacer → bottom sentinel → {belowRows} (footer/survey/tail). Byte-for-byte the same sequence the inline ChatPage.tsx block rendered before the split.

Style parity (TranscriptScrollShell.tsx:874-911): ...scrollerStyle (host passes only {paddingBottom:16}) spreads first, then the shell sets flex:1 and the five overflow/gutter/anchor tokens. Shell never claims paddingBottom, host never claims a shell token → computed style identical; property order in a React style object with no key collisions has no effect.

Attributes carried: ref={scrollerRef}, tabIndex={-1}, className="chat-container", aria-label, aria-live="polite", onScroll (page threads onScrollPin). Composition adds no wrapper DOM node, so the virtualizer's sentinel/spacer refs attach to the same real elements and IntersectionObserver behavior is unchanged.

The remaining new files (capture entry, golden-frame script, mutation harness, two test suites) are dev-only verification tooling — not product behavior, and missing-test/coverage concerns are owned deterministically elsewhere in the pipeline.

The discovery pass reported no candidates; falsification found nothing to add.

No findings.

[OPUS-REVIEWED] 5cdb25e

Verdict parsed from the review's SHA-scoped output markers for commit 5cdb25e94afe365942392e6572ecb59e5d1809de.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 5cdb25e94afe365942392e6572ecb59e5d1809de: <one-sentence reason>

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 5cdb25e94afe365942392e6572ecb59e5d1809de and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 5cdb25e

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 5cdb25e94afe365942392e6572ecb59e5d1809de: <one-sentence reason>

@CrysisDeu
CrysisDeu force-pushed the test/chatpage-scroll-shell-pins branch from 5855b49 to 2455c22 Compare September 2, 2026 20:52
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 2, 2026
@CrysisDeu
CrysisDeu force-pushed the test/chatpage-scroll-shell-pins branch from 2455c22 to f95a459 Compare September 2, 2026 22:44
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 2, 2026
Extracts the transcript scroller skeleton (style contract, sentinels,
spacers, older-messages spinner) into TranscriptScrollShell; page content
threads through aboveRows/children/belowRows slots. Behavior-preserving:
the extracted region renders byte-identical DOM.

The net was written FIRST against the inline page, then the extraction
landed under it: a 23-pin source-token recipe suite, a mounted-DOM render
suite (child order, spinner condition, scroll-event delivery, style
override resistance, slot membership with negative assertions), and a
line-deletion mutation harness (83 targets across the seam; last full
run pre-expansion: 53/53 caught, 0 survivors, split reported as
tests/parse/collection so parse-caught mutants cannot inflate the pin
score). A golden-frame harness (8 self-asserting themed frames incl. a
paging scene) ships alongside; the committed baseline is deferred to a
follow-up capture.

Adversarial review: 4 rounds x 4 cross-vendor blind reviewers
(claude-opus-5 / gpt-5.6-sol / deepseek-3.2 / qwen3-coder-next).
Round 4 verdict: extraction itself clean; all remaining actionable
findings were net-quality items, fixed. 3 findings rebutted with
evidence as false positives.
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the test/chatpage-scroll-shell-pins branch from f95a459 to 5cdb25e Compare September 3, 2026 01:22
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention readiness: passed Eligible automated validation passed for the current revision and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 3, 2026
@bolichen97
bolichen97 enabled auto-merge (squash) September 3, 2026 09:23

@bolichen97 bolichen97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving on the strength of a full readiness audit of every open PR against main, not a
line-by-line reading of this diff — recording that plainly so the next reader knows what this
stamp does and does not cover.

Verified against this exact head SHA:

  • readiness: passed present, and PR Readiness — the one required status context on main
    (ruleset protected-branches) — is success on this head.
  • No check run on this head is failure, cancelled, timed_out or still in flight. Skipped
    jobs are path-filtered conditionals, none of them required.
  • mergeable: true, and the head is not far enough behind main for its green CI to describe a
    base that no longer exists.
  • No surviving reviewer CHANGES_REQUESTED: any such review is on an older commit and therefore
    already dismissed by dismiss_stale_reviews_on_push.
  • Every issue comment, inline review comment and review thread was read and classified. Nothing
    left is an unresolved human change request — the remainder is bot review-lane output, resolved
    or outdated threads, explicitly non-blocking suggestions, and author status notes.

Auto-merge (squash) is armed, so this lands once every other ruleset requirement is met.

@bolichen97
bolichen97 merged commit 0ee0da3 into main Sep 3, 2026
100 of 103 checks passed
@bolichen97
bolichen97 deleted the test/chatpage-scroll-shell-pins branch September 3, 2026 09:30
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Sep 3, 2026
@bolichen97

Copy link
Copy Markdown
Collaborator

Open PR relationship audit

This is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion.

Relationship findings

  • PR #4085 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #4085: REBASE. The rebase is not mechanical here: it needs a decision on PR #7977's pinned recipe string, and that decision belongs in this PR rather than being discovered by red CI. Files: website/src/pages/chat/TranscriptScrollShell.tsx.

No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants