Two owner-reported symptoms, one mechanism (verified frame-by-frame on a zero-model-call fixture): the position machine read its OWN growth-driven scroll lag as the learner leaving. 1. Probe arrival at the bottom: the form opened, the reserve grew, the smooth catch-up lagged (d peaked ~405px > COLLAPSE_DIST 96) → phantom fold → bar shrink morph → re-expand at bottom. FORM→BAR→FORM, ~600ms. Fix: the at-bottom arrival now runs under the SAME fold suppression the teach reveal uses (suppressFoldDuringReveal, extracted) — the arrival's open and its scroll-to-bottom are the same motion class. 2. Probe arrival scrolled up: one stale-open frame painted before the after-paint effect collapsed it. Fix: probeArrivalExpanded — the open/closed state decided DURING RENDER (open iff at bottom), like the teach seam; the effect keeps only the reveal scroll. 3. Dictation stop flush into the pending question: the field grows on its own render (capped 192px), the reserve lands on the NEXT DockOverlay render (~1s later live), and the spacer's growth spiked d≈152 → fold → bar → re-expand. Fix: the offer stamps the geometry mute when the reserve GROWS while pending — the spacer is the phantom-scroll source, so the mute covers exactly its scroll events. Shrinks never stamp (the resize round's stuck-collapsed regression). Live-verified on :3001 (job-settle fixture, abort-channel settle, no model calls): A at-bottom arrival FORM-only; B scrolled-up arrival straight to BAR with zero FORM frames and no scroll-down; C dictation flush FORM-only through the +152px reserve growth.
62 lines
2.6 KiB
TypeScript
62 lines
2.6 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import { probeArrivalExpanded, revealArrival, shouldStampGeometryMute } from "../components/chat.tsx";
|
|
|
|
/**
|
|
* Teach-question arrival states (owner ruling, scroll-honesty round
|
|
* 2026-09-25). Applied DURING RENDER before the first paint — the bug was
|
|
* the stale expanded=true left by the previous question painting one
|
|
* frame of the open question: the dock measured itself tall, reserved that
|
|
* height permanently (fold immunity refused the give-back), and the
|
|
* transcript scrolled as if the collapsed question had opened.
|
|
*/
|
|
/**
|
|
* The geometry-mute's growth rule (flicker round, 2026-09-26): reserve
|
|
* GROWTH while a question is pending is the dock stretching itself (the
|
|
* dictation flush landing many dictated lines at once, reproduced d≈152 >
|
|
* COLLAPSE_DIST) — the spacer's scroll catch-up must not read as the
|
|
* learner leaving. Shrinks are the fold's own give-back and never stamp.
|
|
*/
|
|
describe("shouldStampGeometryMute — when a block resize is our own stretch", () => {
|
|
it("growth while a question is pending stamps (dictation flush, arrival stretch)", () => {
|
|
assert.equal(shouldStampGeometryMute(545, 393, true), true);
|
|
});
|
|
|
|
it("sub-pixel growth does not stamp (rounding noise)", () => {
|
|
assert.equal(shouldStampGeometryMute(394, 393, true), false);
|
|
});
|
|
|
|
it("shrink never stamps — the fold's own morph must not self-mute the re-expand", () => {
|
|
assert.equal(shouldStampGeometryMute(40, 545, true), false);
|
|
});
|
|
|
|
it("no growth stamps without a pending question (composer life)", () => {
|
|
assert.equal(shouldStampGeometryMute(545, 393, false), false);
|
|
});
|
|
});
|
|
|
|
describe("revealArrival — how a teach question lands", () => {
|
|
it("fresh arrival after the composer: collapsed behind the reveal button", () => {
|
|
assert.deepEqual(revealArrival(false), { expanded: false, revealed: false });
|
|
});
|
|
|
|
it("queued behind another question: open — the reading already happened", () => {
|
|
assert.deepEqual(revealArrival(true), { expanded: true, revealed: true });
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Probe-question arrival state (flicker round, 2026-09-26): open iff the
|
|
* learner is following at the bottom; scrolled up in history → arrive as
|
|
* the collapsed bar, decided DURING RENDER (the old after-paint effect
|
|
* painted one stale-open frame first).
|
|
*/
|
|
describe("probeArrivalExpanded — how a probe question lands", () => {
|
|
it("at the bottom following along: open", () => {
|
|
assert.equal(probeArrivalExpanded(true), true);
|
|
});
|
|
|
|
it("scrolled up in history: arrive already-collapsed (owner ruling)", () => {
|
|
assert.equal(probeArrivalExpanded(false), false);
|
|
});
|
|
}); |