Files
buzz/preview-features.json
npub1jmc9dt2lyvzu3h0kxlwxt5zg4fxp9476awyxw6gwxn72g6cw7exqs64whmandtlongwell-block c4176a6e9d feat(desktop): wire settleGate preview flag + synchronous at-rest re-anchor
Register the `settleGate` preview feature (preview-features.json → Settings →
Experiments) and read it in MessageTimeline via `useFeatureEnabled`, passing
`settleGate: { enabled }` to `useAnchoredScroll`. Default-off: the flag-off
runtime path is unchanged from main — the mid-history prepend re-anchor runs
immediately on the prepend commit, exactly as it always has.

Gate-on, the re-anchor is now routed on live-motion at re-anchor time rather
than deferred unconditionally:

- At rest (no scroll event within SETTLE_MOTION_WINDOW_MS): `applyReanchor()`
  runs synchronously in the layout effect, main-identical — no frame ever
  paints at the displaced position.
- Under a live fling: defer behind `observeScrollSettle` (k consecutive still
  frames), re-measuring drift at settle time.

The still-vs-moving decision reads `lastScrollTsRef`, stamped synchronously in
`onScroll`, so it costs zero deferred frames — a rAF probe would leak one
painted frame at full prepend height at rest. SETTLE_MOTION_WINDOW_MS (~6
frames) sits above WebKit's ~2-frame coalesced freeze, consistent with the k>=3
clamp, so a coalesced gap can't misread a live fling as still. A newer prepend,
channel switch, or unmount cancels any pending correction.

This clears the gate-on behavior-contract tier symmetrically: the earlier
defer-always version red-lined timeline-no-shift.spec.ts:429 at maxDrift 3400px
(one painted frame at rest); the synchronous at-rest path takes it to 18px with
no test change. Full timeline-no-shift contract file 5/5 green with the flag
forced on by the e2e bridge. Unit tier green (7 observeScrollSettle cases incl.
freeze-survival and k-clamp); tsc + biome clean.

Ruling: Dawn on :429 (at rest, "never paints a displaced frame" is the
behavior; synchronous-commit is main's means, not the assertion); ratified by
Eva. The momentum-only deferral is real behavior but unexercised by any current
top-tier test — a future mid-fling contract test gets the settle-sampled shape
from birth rather than rewriting :429.

Co-authored-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
2026-07-09 11:08:54 -04:00

54 lines
1.4 KiB
JSON

{
"version": 1,
"features": [
{
"id": "workflows",
"name": "Workflows",
"description": "YAML-defined automations with approval gates",
"platforms": [
"desktop"
]
},
{
"id": "projects",
"name": "Projects",
"description": "Git repository browser and collaboration",
"platforms": [
"desktop"
]
},
{
"id": "pulse",
"name": "Pulse",
"description": "Activity feed with notes, social posts, and agent activity",
"platforms": [
"desktop"
]
},
{
"id": "workspaceRail",
"name": "Workspace Rail",
"description": "Far-left rail for switching relays with cross-relay unread indicators",
"platforms": [
"desktop"
]
},
{
"id": "forum",
"name": "Forum Channels",
"description": "Forum-style threaded channels for long-form discussions",
"platforms": [
"desktop"
]
},
{
"id": "settleGate",
"name": "Scroll settle gate",
"description": "Defer the mid-history re-anchor until scrolling settles (k consecutive still frames) instead of correcting mid-fling — smoother momentum scrolling on WebKit. At rest, identical to today; only under a live fling is the reading row held until the fling ends, then snapped once at settle.",
"platforms": [
"desktop"
]
}
]
}