Commit Graph
1347 Commits
Author SHA1 Message Date
npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6andTyler Longwell b0005dfc49 test(desktop): media-corpus classifier — media reflow is absorbed-class under w4a-gate-1
Answers Eva's leg question (thread 0a496379): are the felt −240-class media
reflow snaps on scroll-up momentum-class (killed by w4a-gate-1), walk-blind
(RO-path, a separate leg), or SHRINK-class (needs Max's pre-realization band)?

Adds a committed media corpus + classifier so the answer is reproducible from
the tip on a fresh build, mirroring the jitter/fast-classify arm.

Corpus (e2eBridge `mediaCorpusBody`, channel `media-corpus`, 400 rows, 8 kinds).
Row-height error is MEDIA reserve-vs-true mismatch, not text mis-estimation,
from three in-source-verified divergence sources:
- Link-preview cards reserved flat at PREVIEW_CARD=70 but rendered taller (GROW).
- Width-clamp images: CORRECT dims, but the estimator hardcodes MEDIA_MAX_WIDTH=384
  while render clamps to max-w-[min(24rem,100%)]; in a column < 384px the render is
  width-limited shorter than reserved (SHRINK, no lying dim, no decode timing).
- Dim-mismatch images in BOTH directions: overstate height (SHRINK) and understate
  (GROW), so the corpus CAN produce SHRINK — the GROW-dominant prediction is
  falsifiable, not baked in.
No dim-less band: that path is pinned to a fixed 256px box (reserve==mount) and
provably cannot reflow. Images use a tiny inline PNG data-URL so they realize
without network. (Eva verified the mechanism chain independently in mainbase
2cc0eb53.)

Fixture (upscroll-media.perf.ts) clones the fast classifier verbatim — probeLen
append-count join, SKIP/GROW/SHRINK/UNATTRIBUTED discriminator, dev, ±2-frame
firedNear admission — and only:
- navigates to channel-media-corpus, then narrows the viewport to 450px so the
  timeline column lands below 384px; asserts the measured media-container width
  is < 384 so the width-clamp SHRINK source provably bites (else it fails loudly);
- adds a MEDIA-REFLOW CENSUS: for every above-anchor reflow (|signedShift|>3px),
  whether it landed on a tracked in-band row and the worst (most-backward) rowMove
  across it, collapsing a persistent gate-held reflow's per-rAF re-measure into one
  run so a stuck 198px GROW isn't inflated into hundreds;
- asserts MEDIA LIVENESS (>=1 in-band reflow) so a zero-reversal result cannot be
  a dead corpus; drops the fast arm's decomposition self-test (that re-proves the
  gate mechanism the jitter arm already ratifies — off-charter here).

Result, fresh w4a-gate-1 build, BOTH engines, reproducible from this tip:
- Chromium: 0 reversals. 9 reflow runs (5 in-band), 7 GROW / 2 SHRINK. Every
  in-band reflow worstRowMove >= 0 — incl. a persistent +198 GROW held 170 frames
  at rowMove 0.0.
- WebKit:   0 reversals. 8 reflow runs (4 in-band), 6 GROW / 2 SHRINK. GROW runs
  gate-SKIPed to rowMove 0.0; SHRINK runs (−14.5) fired-and-corrected to +0.5
  (forward).

CLASSIFICATION: the media reserve-vs-true reflows are momentum/absorbed-class —
the landed w4a-gate-1 fix + correction eat them; not one renders a felt backward
snap on either engine. NOT walk-blind, NOT needing Max's pre-realization band.
Neither Dawn's GROW-likely nor Eva's SHRINK-likely prediction "wins" in the felt
sense: both classes fire, both are absorbed.

HONESTY BOUND: this is a deterministic proxy for media reflow, not a claim to
reproduce Tyler's exact frames. His live trackpad still owns acceptance; #1662
stays draft.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-09 02:09:16 -04:00
npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6andTyler Longwell 2bd2076ebb fix(desktop): rekey upscroll momentum gate onto rendered scroll (w4a-gate-1)
The mid-history correction's momentum-skip gate keyed on the raw
`scrollTop` delta since baseline. On WebKit, momentum is coalesced into
`scrollTop` on the compositor's own async clock, so a frame that PAINTED
still can read a large raw delta and wrongly skip a genuine above-anchor
reflow — the reversal survivor bin (the 204px-class lurch of #1662).

Rekey the gate onto the RENDERED scroll — the painted wheel motion this
frame — `renderedScroll = aboveShift - ΔtopOffset`: the anchor's painted
on-screen move with the reflow's own push removed, both terms read from
the painted DOM (getBoundingClientRect), not the possibly-ahead
`scrollTop`. A rendered-still frame now reads ~0 and the reflow is
corrected instead of dropped.

Because the gate now needs `aboveShift` to decide, the band walk runs
before the gate. To keep "under-correcting is invisible; the next quiet
frame catches up," `sumAboveAnchorShift` no longer refreshes the height
cache inline — it STAGES the refresh and returns `{ aboveShift, commit }`.
The caller applies `commit()` only past the gate, so a momentum skip
leaves the cache for the next frame; a kept correction refreshes it as
one indivisible pass (the "second observer no-ops" invariant is intact).

Arbiter contract, fresh w4a-gate-1 build, both engines:
- raf-correction HARD gate: Chromium 0 reversals, rAF fires 0 / RO 21
  (RO sole writer); WebKit 2 reversals max 14.5px (<=34 quantum floor),
  rAF fires 24 / RO no-op (residual 0.0). Ratified invariants hold.
- slow-scroll: 0 reversals both engines (felt low-velocity regime clean).
- fast-classify DECOMPOSITION self-test (WebKit): pure-reflow attempts
  median |signedShift|=18.5 but median |renderedScroll|=0.0 — the reflow
  does NOT leak into the gated quantity, and 19/28 fire. Pure-scroll
  |renderedScroll| runs up to 28. The decomposition is proven, not
  asserted. Guarded WebKit-only: on Chromium the rAF is the passive loser
  observer (RO corrects first), so renderedScroll there tracks signedShift
  by construction — characterized, not asserted.

KNOWN RESIDUAL (surfaced, not hidden): 2 WebKit survivors remain, but
they are a DIFFERENT class — signedShift=0.0 (the band walk sees no
above-anchor reflow) while the row still lurches 14.5px. The momentum-gate
survivors the rekey targeted are gone; these are cross-check/null-target
skips where the reflow is invisible to the band walk. Separate diagnosis.

Diagnostic-only `renderedScroll` is threaded through the shared result
type + emitted on the rAF probe so the fixture can prove the
decomposition; stripped/gated before merge per the file-size exit
condition. Raises the useAnchoredScroll.ts size exception 1180->1210 to
cover the arm (biome reflows the 4-prop returns); flagged in the override
comment for Eva as heavy-for-a-diagnostic.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-09 01:11:30 -04:00
npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6andTyler Longwell 44d9edcf2f chore(desktop): raise useAnchoredScroll size limit 1140->1180 for W4a chase
The reading-anchor hook is the sole cross-engine scroll writer and the
survivor of the GUI-latency sweep's upscroll-reversal chase. The W4a
instrumentation legitimately inflated it past 1140: the signed-shift
grow/shrink probe (classifier arm) and the renderedScroll gate-clock rekey
(w4a-gate-1 arm), both carrying load-bearing provenance inline.

Per Eva's ruling, take the scoped override NOW and hold the structural
split until the arms in flight against this file settle — splitting a hook
whose provenance comments are themselves load-bearing (the anchor-margin
lesson lives in it) mid-chase is how a guard gets stripped. Exit condition
recorded at the override site: once #1662's fate is decided, the diagnostic
emits get stripped or gated and the mid-history corrector + band walk split
into a sibling module.

This also retires the --no-verify size bypass the classifier commit
(9df6d92e, 1156 lines) shipped under: the limit now covers it honestly, and
Sami independently flagged the same blocker on the branch.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-09 00:49:20 -04:00
npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6andTyler Longwell 9df6d92e69 test(desktop): widen classifier attribution window to +/-1 frame
The two rAF loops (fixture sampler + hook) interleave in an order we don't
control, and a late WebKit RO appends a frame after the reflow paints. A tight
[prev, this) window mis-labeled all 4 fast-corpus survivors UNATTRIBUTED when the
attempt had appended one frame late. Widening to [prev, next) resolves them to
their true class. Fast jitter corpus, WebKit: all 4 survivors classify SKIP
(fired=false, signedShift=0.0, dScroll=0.0) — the momentum-skip gate suppressed
the write on a coalesced-momentum still frame, so the reflow renders uncorrected.
Neither GROW (absorption) nor SHRINK (Max's band): the lever is the skip gate.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-09 00:33:06 -04:00
npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6andTyler Longwell a4fdc35fa9 test(desktop): grow/shrink/skip reversal classifier + signedShift probe field
Adds the discriminator Sami specced (thread 5b46582e): the escape counter is
pure rendered rowMove and residual is |aboveShift|, so neither can recover the
grow-vs-shrink topology that decides the fix. Thread signed aboveShift (rAF path)
/ signed drift (RO path) through the correction probe as a diagnostic-only
'signedShift' field alongside wouldFire; production behavior unchanged.

The slow-scroll classifier joins each reversal to the correction attempt(s) in
its frame window (append-count join, order-robust across the two rAF loops) and
classifies: SKIP (gate/xcheck suppressed the write -> uncorrected reflow),
GROW (fired, signedShift>0 -> write is the felt snap, absorption's topology),
SHRINK (fired, signedShift<0 -> reflow renders it pre-write, only smaller
per-frame realization helps). Bumps ANCHOR_BUILD_STAMP to w4a-classifier-1 so the
stale-dist guard rejects any bundle missing signedShift.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-09 00:29:47 -04:00
npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6andTyler Longwell 26418e92f5 test(desktop): slow-scroll characterization leg (2-4px/frame momentum-decay)
Drives the low-velocity + momentum-tail regime Tyler reports the residual in,
which the constant-12px/32ms gate does not cover. Characterization only — logs
the reversal distribution and still-frame count, no pass/fail ceiling, since the
slow-regime floor is what we are measuring. Keeps liveness + stale-dist guards.

Honesty bound documented in-file: Playwright mouse.wheel is synthetic and cannot
reproduce a real trackpad's compositor-coalesced momentum, so a green run means
'the correction is not a large felt jump at low synthetic velocity', not
'reproduces everything Tyler feels'.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-09 00:22:06 -04:00
npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6andTyler Longwell e7243887e4 fix(desktop): correct realizing-upscroll reading-row reversal on both engines
Realizing upscroll made the reading row jump down then snap back — a
reversal frame pair, most visible on WebKit where the ResizeObserver for
a content-visibility realization delivers one frame LATE (paint N, RO
N+1). This lands design-of-record (c): a single ungated RO mid-history
corrector plus a per-rAF sampler, coordinating ONLY through a shared
row-height cache — no engine branch. The on-time observer per engine is
the sole writer:

  - Chromium: the RO delivers pre-paint, so the ungated RO corrects at N
    (by the anchor's own measured drift) and refreshes the cache; the
    rAF then sees residual ~0 and no-ops.
  - WebKit: the RO is late, so the rAF (whose synchronous
    getBoundingClientRect forces this frame's layout) corrects at N; the
    late RO no-ops against the rAF-refreshed cache.

The RO path is deliberately UNGATED by the rAF path's observed/above
agreement cross-check: that gate suppresses the rAF band walk's
straddler-miscount fabrication, a risk the RO does not have (its entries
are ground truth for which rows resized). Applying the gate to the RO
strangled the on-time Chromium observer and regressed it to 16
reversals. Straddler safety on the RO path comes instead from the
safe-margin reading anchor (snapshotReadingAnchor +
READING_ANCHOR_SAFE_MARGIN_PX), which sits a notch below the realization
band so a row realizing across the fold is above the anchor and summed
into the drift correctly — documented as a load-bearing invariant at the
site so a later refactor cannot strip it thinking the `changed` trigger
covers straddlers (it does not; the margin does).

Own red/green E2E fixture (upscroll-raf-correction.perf.ts), verified on
both Playwright engines against a stamped build:

  Chromium   0 reversals  max 0.0px   RO 21  rAF 0
  WebKit     2 reversals  max 14.5px  rAF 24 RO 0 (residual 0.0)

Asserts are a layered stack so a vacuous pass cannot slip through: a
build-stamp stale-`dist` guard, LIVENESS (some correction fired),
PRIMARY correctness (reversals <= 4, maxReversalPx <= 34, engine-
agnostic), and per-engine MECHANISM in both directions (chromium
roFires>0 && rafFires===0; webkit rafFires>0 && maxRoResidual<=0.5).
Layering self-test confirmed: disabling the ungated-RO write flips
PRIMARY red at 16 while LIVENESS stays green.

The hook is the sole cross-engine reading-anchor scroll writer and its
correctness lives in the per-site provenance comments, so it crossed the
1000-line file-size limit (888 at HEAD); added a documented, narrowly-
scoped override queued to split the mid-history corrector into a sibling.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 22:38:21 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell 2c422c1e34 docs(desktop): correct totalDrift comment — summed-magnitude of all anti-scroll motion, not the ~220px non-recovering subset
Quinn's correctness flag: the shipped comment framed totalDrift as the
non-recovering-under-correction accumulation (~220px), which the measured
1042.81 baseline disproves — it includes recovering-flash reversal halves
and multi-frame realizations too. Comment now states what it measures.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 20:40:56 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell dfa9b0cd10 Merge W4 felt-gate instrument: three-leg jerk metric + whole-run cumulative drift (sami/webkit-comp-gate @ 45500ed1)
Lands the ratified W4 gate in-tree: peak-jerk + windowed signed-drift
asserting, rms-jerk logged, whole-run cumulative anti-scroll drift as
the locked W4a A/B reference (WebKit 1042.81 vs Chromium 0.00 control).

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>

* commit '45500ed1':
  W4 felt gate: jerk metric rewrite + whole-run cumulative drift instrument
2026-07-08 20:40:08 -04:00
npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57ccandTyler Longwell 45500ed11c W4 felt gate: jerk metric rewrite + whole-run cumulative drift instrument
Replaces the original deviation-vs-appliedScroll W3 metric with the
ratified W4 three-leg jerk contract (RESEARCH/FELT_WHEEL_GATE_METRIC_W4.md,
Quinn) and adds the whole-run cumulative anti-scroll drift instrument
Eva/Quinn asked for as the W4a A/B reference scale.

Metric (all on the reading row's per-frame rect.top, no scrollTop / no
dispatched-delta reference — coordinate-free by second-differencing):
- LEG 1 peak jerk = max |second diff| — the felt-lurch spike. Gate <= 8px.
- LEG 2 signed anti-scroll drift over a trailing 100ms window — the
  skip-forever guard (catches a sustained reversal by sign). Gate <= 12px.
- LEG 3 rms jerk (chatter) — LOG-ONLY behind BUZZ_PERF_GATE_RMS_JERK=1;
  no A/B separation reachable under discrete actuation (structural, thread).
- LEG 2 cumulative drift (NEW) — same anti-scroll component summed over the
  whole run, no window. LOG-ONLY, never gated. The windowed gate is the
  felt-relevant burst; this is the W4a reference scale that the 12
  non-recovering under-corrections accumulate into. The pre-realization
  band (W2) should collapse THIS number.

Harness fixes for small-STEP runs (both were false-fails, not scorer bugs):
- velocity anti-cheat floor scales with STEP (0.008*STEP_PX) — was a fixed
  0.1 pinned at STEP=12, false-failed STEP=2.
- test timeout 300s -> 900s (408 events/swipe at STEP=2 is slow).

Verified both engines against Dawn's shipped writer e7297849 (SWIPES=12,
STEP=2): Chromium 2.00 / 0.00 / cumulative 0.00 (clean PASS, control);
WebKit peak 37.55 / windowed 27.55 / cumulative 1042.81 (exposed W4a
frame-late layer). Chromium cumulative = 0.00 validates the instrument:
the quantization staircase is pure forward scroll, so cumulative is
entirely the WebKit reversal signal — clean control(0)/signal(1042).

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 20:33:50 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell f196ff7a2d Merge W1r4: R_obs agreement gate — Shape-B fabricated-lurch fix (dawn/webkit-compensation @ e7297849)
Fallback fires only when |aboveShift - observedShift| <= 0.5 against a
coherent prev-rAF snapshot, corrects by observedShift. Kills the
fabricated e=0 rowMove=+35/41 shoves (Shape B). Residual frame-late /
multi-frame-blind escapes are W4a (RO lands post-paint on WebKit CV
realization).

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>

* origin/dawn/webkit-compensation:
  fix(desktop): gate WebKit upscroll fallback on prev-rAF anchor agreement
2026-07-08 20:10:30 -04:00
e729784935 fix(desktop): gate WebKit upscroll fallback on prev-rAF anchor agreement
The prior fallback fired `scrollTo(currentScrollTop + aboveShift)` whenever
the baseline-relative position diff read 0 and `|aboveShift| > 0.5`. On WebKit
that condition is not specific to a real reflow: the per-rAF baseline is
captured post-realization, so `baseline` and `current` straddle the SAME side
of the reflow (dTop=dScroll=0) on EVERY frame, and `aboveShift` — summed from
the RO entries — can be nonzero on a frame where the anchor never moved,
because a row straddling the anchor boundary is miscounted. The fallback then
fabricated a downward shove: the "Shape B" slow-trackpad lurch
(`e=0, rowMove=+35..41px, dScroll=0`) — firing the correction WAS the visible
jump.

Add a second, baseline-timing-independent signal and fire only when the two
instruments agree. Carry the anchor snapshot from the PREVIOUS rAF tick
(`prevReadingAnchorRef`), which — unlike the same-frame baseline — was captured
before this frame's realization and so spans the reflow. Its document-position
shift `observedShift = (scrollTop+topOffset) − prev(scrollTop+topOffset)` is
scroll-invariant (user scroll moves scrollTop and topOffset equal-and-opposite),
so it isolates the reflow alone (Eva's probe `e`, from one coherent snapshot;
Quinn's scroll-isolated `R_obs`). Fire only when `prev.id === baseline.id` AND
`|aboveShift − observedShift| ≤ 0.5`, and correct by `observedShift` (never raw
screen motion — that would refold the user's scroll into the pin).

Agreement is the classifier boundary. Two-population split of fallback frames
on the WebKit slow-trackpad probe:

  population         | aboveShift vs observedShift | old writer   | gated writer
  -------------------|-----------------------------|--------------|-------------
  real reflow        | agree (Δ ≤ 0.5px)           | corrects OK  | corrects OK
  straddler miscount | disagree (Δ = row height)   | fabricates   | suppressed
                     |                             | +35..41 shove|  (no write)

Suppressing the miscount branch removes the fabricated shoves without touching
the frames where the RO sum is genuine.

Validated (each run x2, stable):
- Shape-B fabricated `e=0` shoves: eliminated (was the dominant felt defect)
- Chromium slow-trackpad: 0 escapes, sync 0.00, felt 0.00 (no regression)
- WebKit felt/wheel gate: 0.91px peak (<< 2px threshold), was 0.00 pre-gate —
  the 0.91 is the frame-late CV-realization flash the miscount was masking
- WebKit slow-trackpad escapes: 67 -> 54
- 2167/2167 unit tests, tsc, biome clean

Residual (54 escapes) is a different phenomenon with a different owner: ~15
self-correcting frame-late flashes + ~12 multi-frame-blind under-corrections,
both rooted in WebKit delivering the CV-realization RO callback one frame after
the realization paints (documented in
RESEARCH/WEBKIT_RO_REALIZATION_FRAME_LATE_W1.md; overturns W4a doc lines 96-98,
which assumed the RO write is same-frame-visible on WebKit). That is a W4a
architecture item (Quinn owns direction; Max's W2 pre-realization band is the
A/B candidate), not a W1 branch-logic defect.

Co-authored-by: Dawn <dawn@sprout-oss.stage.blox.sqprod.co>
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-08 19:57:00 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell 8239284d92 test(desktop): slow-trackpad mismatch probe (per-frame rect.top trajectory)
Tyler's on-device verdict at 3d1e91ea: better but not perfect. This probe
actuates the shape of his input — small (12px) wheel deltas at slow (~32ms)
cadence, no settling — and samples the tracked reading row's rect.top,
scrollTop and mounted count EVERY rAF, so one-frame events are visible.

Flags frames whose rowMove escapes the slow-wheel staircase envelope
(reversal <= -3px, or shove >= 2*delta+3px), pairs escapes that cancel
within 3 frames (one-frame flash of a deferred correction) vs unpaired
(felt lurch), pure rect.top in the flag condition per the W4 framing.

At tip b170680d (includes W1r3 fe786ea4): Chromium 0 escapes / 3447 frames;
WebKit 81 escapes / 1754 frames — 11 flash pairs (-27.5 shove then +28
correction one frame later) + 59 unpaired single-frame jumps of exactly
35px / 41px recurring every ~6 rows. Diagnostic, non-gating.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 19:23:25 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell b170680d8f Merge W1r3: hold reading row across WebKit realization via aboveShift fallback (dawn/webkit-compensation @ fe786ea4)
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>

* origin/dawn/webkit-compensation:
  fix(desktop): hold reading row across WebKit realization (engine-order-independent)
2026-07-08 19:12:23 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell 3d1e91ea03 Merge W1r2: drop redundant post-correction re-baseline (dawn/webkit-compensation @ c9e5bf3c)
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>

* origin/dawn/webkit-compensation:
  fix(desktop): drop redundant post-correction re-baseline (single-writer)
2026-07-08 19:04:15 -04:00
fe786ea40e fix(desktop): hold reading row across WebKit realization (engine-order-independent)
The upscroll compensation writer diffed the reading anchor's document
position against a per-rAF baseline. On WebKit the rAF baseline is
captured AFTER content-visibility realization has already shifted the
row, so baseline and current straddle the same side of the reflow
(dTop=dScroll=0) and the position diff is blind to it — the correction
never fired and the row took the full raw realization shift.

Add a second, baseline-timing-independent signal: when the position
diff sees no move, fall back to the net height change of the RO entries
ABOVE the anchor (`aboveShift`) and absorb it into scrollTop. This reads
the reflow magnitude directly from the resize entries rather than
diffing a snapshot WebKit captured too late, so it holds under WebKit's
async realization ordering without depending on scroll-event timing
(which coalesces off the scrolling thread under real wheel input).

Validated (each run x2, stable):
- WebKit felt/wheel (real input): 0.00px peak deviation (was ~200px)
- Chromium felt + sync: 0.00px, no regression
- 8/8 unit tests, tsc, biome clean

The WebKit synchronous-actuation gate still reads ~44px peak; that is an
`el.scrollTop=x`-vs-CV-realization artifact of the imperative test path,
not felt jitter — the felt-mode (wheel) gate under design supersedes it.

Co-authored-by: Dawn <dawn@sprout-oss.stage.blox.sqprod.co>
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-08 19:00:41 -04:00
tlongwell-blockandDawn c9e5bf3cac fix(desktop): drop redundant post-correction re-baseline (single-writer)
The RO compensation callback re-snapshotted `readingAnchorRef` right
after its `scrollTo`, but the per-rAF loop is documented as the single
writer of that ref and re-snapshots from live geometry next frame
(~16ms), already reflecting the correction. The extra write made the
"single writer" invariant false for no behavioral gain and cost one
`snapshotReadingAnchor` row-walk per correction. Remove it so the
invariant holds literally.

No behavior change: the next rAF re-baselines regardless. Unit tests
(computeAnchorCorrection sign + invariance) unaffected, 8/8 green.

Follow-up to Quinn's W1 cross-review (hole #2).

Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
2026-07-08 18:25:50 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell eaccc0fca5 Merge W3: offline trackpad-momentum upscroll gate (sami/webkit-comp-gate @ 7c258747)
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>

* commit '7c258747':
  W3 trackpad-momentum upscroll GATE: merge-blocking, offline, both engines
2026-07-08 18:18:26 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell 6ef18806ad Merge W1: engine-order-independent upscroll compensation (dawn/webkit-compensation @ 6b04aa1e)
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>

* commit '6b04aa1e':
  fix(desktop): make upscroll compensation engine-order-independent (WebKit)
2026-07-08 18:18:25 -04:00
npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57ccandTyler Longwell 7c2587472c W3 trackpad-momentum upscroll GATE: merge-blocking, offline, both engines
The live trackpad probe (upscroll-trackpad-live.perf.ts) proved the WebKit
compensation defect against real #buzz-bugs but needs staging + a
port-forward + a member nsec, so it can never gate a PR merge. This gate
reproduces the SAME felt-lurch metric under the SAME momentum actuation on
the deterministic mock jitter-corpus bridge — no network, greppable in CI,
on BOTH engines.

Covers both lurch sources (W3 trace, thread):
- CV-realization lurch: jitter-corpus is the heterogeneous 400-row seed
  whose estimateRowHeight mismatch T1.1/T1.2 already gate.
- prepend-commit-under-momentum: 400 rows > 300 CHANNEL_HISTORY_LIMIT, so
  fetchOlder pages behind an until-cursor. useLoadOlderOnScroll fires off an
  IntersectionObserver on a 600px-margin top sentinel — pure geometry,
  input-cadence-independent — so momentum triggers it like live.
  channelWindowDelayMs=1000 defers the commit so it lands frames later,
  under momentum, reproducing the commit race (not an instant same-frame
  commit).

Engine fidelity mirrors T1.1/T1.2: overflow-anchor forced 'none' so
Chromium reproduces the shipped WKWebView (no native scroll anchoring);
under playwright.perf-webkit.config.ts this is the real WebKit family.

Three assertions (Eva's thresholds + Quinn's W4 caveat):
- peak felt lurches (|dev|>10px, non-commit) == 0 — the wheel-fight spike.
- smooth frames (|dev|<=1px) >= 99%.
- RMS deviation across the run <= placeholder — the skip-forever guard:
  a too-tight staleness skip-bound never fires the correction under
  momentum, showing as a run of sub-10px deviations that dodge the peak
  gate but sum to main's raw-shift drift. Peak is blind to that shape.

RED at contract 6b9203ca under the WebKit mirror is mandatory and verified:
WebKit 132 lurches / 94.42% smooth / RMS 4.24px; Chromium-with-mirror 131
lurches / 97.87% smooth. Dawn's engine-order-independent W1 fix turns it
green on both engines. Exact lurch and RMS ceilings pinned against Dawn's
first green candidate, same discipline as T1.2.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 18:14:46 -04:00
tlongwell-blockandDawn 6b04aa1e49 fix(desktop): make upscroll compensation engine-order-independent (WebKit)
The T2 realization-compensation writer in useAnchoredScroll.ts was
near-perfect on Chromium and near-ineffective on WebKit (39 felt lurches
up to 204px per 30-swipe momentum pass). Root cause: the reading-anchor
baseline was snapshotted in the scroll handler, on the assumption that
scroll fires before ResizeObserver in the same frame. That holds on
Chromium; WebKit dispatches scroll asynchronously off its scrolling
thread and coalesces it, so under momentum the RO consumed a stale
snapshot and the drift measurement folded the user's own wheel delta
into the correction — the writer fought the wheel.

Make the path independent of scroll-event timing:

- Baseline the reading anchor per-rAF (single writer, module rAF loop),
  not per-scroll-event. rAF runs before layout/RO on every engine, so
  the baseline is the freshest pre-reflow read cross-engine.
- Compute the correction scroll-invariantly from the anchor row's
  document position (scrollTop + topOffset), which changes only when
  content above reflows — isolating the layout shift from user scroll.
  Extracted as computeAnchorCorrection(), unit-tested for sign and
  invariance.
- Write an absolute scrollTop target from this frame's own late reads,
  not scrollBy against a possibly-stale offset (blunts the write-race).
- Staleness is a first-class SKIP: if scroll moved more than
  COMPENSATION_SCROLL_SKIP_PX since baseline, momentum is in flight and
  the reads may straddle a compositor commit — skip and let the next
  quiet frame catch it. Under-correcting is invisible; fighting the
  wheel is the lurch.
- Derive at-bottom vs mid-history from synchronous geometry
  (isAtBottomNow) in both the rAF sampler and the RO callback, never
  from the scroll-maintained anchorRef, so the branch and the baseline
  can't disagree under momentum.
- Pick the reading anchor a safe margin below the fold
  (READING_ANCHOR_SAFE_MARGIN_PX) so it never sits inside the
  realization band it's meant to measure against.

Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
2026-07-08 18:13:49 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell 6b9203cad7 test(desktop): live-staging upscroll probes + WebKit perf project
Two live probes that run the built GUI against a real relay (port-forward
+ Host rewrite) and walk real scrollback in a real channel:

- upscroll-1px-live.perf.ts: settled steps (BUZZ_PERF_STEP_PX), scores
  per-step row movement through real fetchOlder loads, measures the felt
  jump at each prepend commit.
- upscroll-trackpad-live.perf.ts: macOS trackpad replica — wheel events at
  ~8ms spacing in finger-ramp + momentum-decay swipe profiles, no settling,
  with a per-RAF sampler scoring tracked-row motion vs applied scroll.

Plus playwright.perf-webkit.config.ts: the same probes on Playwright
WebKit. This is the config that exposed the T2 compensation being
near-ineffective on WebKit-family engines under momentum input while
Chromium scored clean — Chromium-only probing cannot see the defect class
the shipped WKWebView app exhibits.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 18:01:52 -04:00
104f3cd62e T1.2 upscroll-jitter gate: score a fetchOlder prepend per run
The committed gate certified only the CV-realization half of H1: it
terminated at the ~600px fetchOlder sentinel band, so the pagination
half — cold rows paged in ABOVE the viewport by scrollback — was proven
only by Quinn's throwaway probe, not by the gate as it runs (the
coverage gap Quinn [9] and Dawn [10] flagged, land-now/fast-follow
ratified by Eva [11]).

Root cause: when a prepend lands, scrollTop jumps UP (~100 older rows of
height added above), so `appliedTop = before - after` goes negative and
the loop's top-of-history break fired at the sentinel — before scoring
any paged-in row. On the RED baseline this break was deterministic; on
GREEN it was flaky (the prepend was scored only when the re-anchor
happened to leave the tracked row in the safe band).

Fix: distinguish the two causes of a non-decreasing scrollTop by
mounted-count. A prepend grows it; the true top does not. On a prepend
re-anchor, skip scoring that step (its motion is the multi-thousand-px
jump, not a fixed STEP notch — scoring it would poison the metric) and
CONTINUE; the next iteration re-baselines a fresh safe-band row in the
paged-in window and scoring resumes across the prepend. Assert
`prependObserved` so the gate exercises BOTH jitter sources every run.

Verified on the T3 integration tip 1fa4551a (GREEN) and the T1.1 base
32e41e78 (RED), both rebuilt:
- GREEN: prepend scored (50->200, walks all crossings), gate 0.00/0.00
  PASS, 77-78 scored steps, deterministic across 4 runs.
- RED: prepend scored (50->150), coverage assert PASSES, gate still
  FAILS on jitter (peak 41px > 2.0) — reds for the right reason.
The dual-sided RED/GREEN contract and the anti-cheat floors are
preserved; the change is scoped to the gate file only.

Note: gate-run R is window-dependent (Eva/Max [8]); scoring the deeper
paged-in population raises the non-gating R diagnostic from 75 to ~85.
This is NOT a cross-lane leak — the T2 writer's reserve is untouched;
R moved only because the sample window grew, exactly as the
window-sensitivity caveat predicts.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 15:01:06 -04:00
f8adc9f155 feat(desktop): wire measured column width into row estimates + anchor-contract guard
Two of the deferred scroll-jitter seams, on top of the integrated fix train
(T1.1 gate -> T2 writer -> T3 estimator @ 1fa4551a):

- columnWidthPx: measure the timeline row column once (useElementWidth) and
  thread it into timelineRowReserveStyle so row-height reserves use the real
  wrap width instead of the 64-char fallback. Pre-measure zero passes undefined
  to preserve the estimator's own fallback rather than tripping its min-chars
  floor. On jitter-corpus this shrinks estimate-error R from 75 -> 55 peak
  (better reserves = smaller realized-vs-reserved delta) while the motion gate
  stays GREEN 0.00/0.00 -- R moves only in the estimator lane, the gate only in
  the writer lane, cross-lane invariant intact.

- Wren #4 anchor-contract guard: a companion perf test that reads the PRODUCTION
  computed overflow-anchor on the real conversation scroller (no test override)
  and asserts it resolves to none. The jitter gate forces the property for its
  measurement; this proves the shipped stylesheet actually ships it, so a
  regression that hands correction back to Chromium's native anchoring (masking
  a WKWebView-only on-device break) fails loudly.

The onRealizedHeight seam is intentionally NOT wired: T4 ground truth
(RESEARCH/GUI_SCROLL_RECLAIM_T4.md) shows the upscroll path is append-only with
no unmount, so it needs no JS height cache; a per-event-id cache is only the
deferred, narrowly-scoped T4b for the head-refetch case. Adding an unused
callback now would be dead abstraction.

Validation:
- pnpm --dir desktop typecheck: clean
- biome check on both changed files: clean
- rowHeightEstimate.test.mjs 16/16, useAnchoredScroll.test.mjs 2/2
- perf: upscroll-jitter GREEN 0.00/0.00 (R 55), anchor-contract PASS

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 15:01:06 -04:00
npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgytaandnpub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d 2b96e4fbbc T3 tune markdown reserve overshoot
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
2026-07-08 15:01:06 -04:00
npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgytaandnpub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d 92fd1199de T3 shrink timeline row height estimates
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
2026-07-08 15:01:05 -04:00
ae8f130738 fix(desktop): compensate realization jitter on upscroll (T2 writer)
Scrolling UP a fully-loaded channel was subtly jumpy while scrolling down
was smooth. Root cause (Eva's H1): rows above the opening viewport have
never painted, so they sit at their `contain-intrinsic-size` reserve under
`content-visibility: auto`. Scrolling up, each row realizes at its true
height the instant it enters the realization band; the delta (true -
reserve) shifts content above the reading row. On shipped WKWebView there
is no `overflow-anchor` to correct it, so every realization lands raw on
the reading position — one lurch per row.

The fix makes the conversation scroller own its reading position through a
single same-frame compensation writer:

- Snapshot the reading anchor (first fully-visible row + its top offset) on
  every scroll event, before the ResizeObserver fires. The RO runs after
  layout and before paint, so the "before" position can only come from the
  last scroll event — never from inside the RO callback.
- Observe each `.timeline-row-cv` row (CV realization is a resize of the
  row's own box and does not surface as an ancestor resize in Blink), seeded
  at observe time with the browser's current layout height read from
  `contain-intrinsic-block-size` — the reserve for an unpainted row. Seeding
  from the reserve is what turns the first realization into a measurable
  `realized - reserve` delta instead of an unmeasurable first sighting.
- On a real height change, re-pin the reading anchor to its saved offset
  with a same-frame `scrollBy(drift)`. A single measured correction: the
  layout engine already summed the above-anchor deltas into the row's top,
  and rows resizing below the anchor don't move it, so they're excluded for
  free. Re-baseline after the correction.
- Force `overflow-anchor: none` on the scroller so the writer is the single
  scroll authority on every engine — Chromium's native anchoring can't
  double-correct behind the writer's back, and WKWebView has none anyway.
  This also aligns CI (Chromium) with macOS (WKWebView), which ran different
  scroll contracts — how the jitter shipped unseen.

Verified against the committed T1.1 gate (upscroll-jitter.perf.ts) on
jitter-corpus cold-load, sync fixed-step actuation, overflow-anchor forced
off: RED without this writer (peak-dev 46 / rms 23), GREEN with it (peak-dev
0.00 / rms 0.00, reproducible), and the non-gating wheel felt-mode pass also
0.00/0.00. The non-gating estimate-error R diagnostic stays at 75 (T3's lane;
this writer compensates motion, it does not shrink the reserve error).

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 15:01:05 -04:00
164408c411 T1.1 upscroll-jitter gate: reframe to motion-consistency (comp-sensitive)
Dawn found the RED gate from 33e73511 was mathematically invariant to the
fix: its residual subtracted the REALIZED scrollTop delta, which includes
the fix-writer's compensating scrollBy, so the writer cancelled out and the
gate scored raw estimate error R (only T3 can move it), not motion
smoothness. Quinn and Eva re-derived and confirmed. Co-visible and any
row-vs-row differential share the disease: compensation is a uniform global
offset, so it cancels out of any quantity not referenced to the input.

Reframe (converged with Dawn/Quinn/Eva; ratified event 63f5e6e1):

- Metric is now per-notch MOTION CONSISTENCY: the reading row's viewport
  motion (rowMove = after.top - before.top; NO scrollTop reference) scored
  as deviation from the run median. This references the row's own motion
  ACROSS notches (temporal self-reference), never a neighbour (spatial) or
  scrollTop, so the writer's scrollBy survives into the metric and is SEEN.
- Actuation is a fixed synchronous step (scrollTop -= STEP) so the input
  delta is constant by construction, bypassing Blink's wheel-scaling (a
  wheel notch applies 218/220/222...; median-of-run would misread that
  per-notch scaling as jitter). Dawn independently confirmed even the
  actuated scrollTop delta is post-writer contaminated (48-75px spread
  under sync) — rowMove is the only clean measurable.
- Anti-cheat floor: mean rowMove must be > STEP*0.75 so a frozen or
  half-applying scroller (near-zero variance, would false-green) is caught.
- Old scrollTop-referenced residual kept as a printed NON-GATING diagnostic
  (= estimate error R = T3's estimator acceptance number).
- Second non-gating diagnostic: one wheel-actuated pass of the same metric
  (Tyler's real input is a wheel), on the record every run.
- RED-at-tip is a HARD gate with a VOID comment: median-of-run is only
  valid while the corpus produces varying realization, and the tip run
  being RED IS the proof of that dispersion.

RED at tip: peak-dev 41px / rms 24px (gate <=2 / <=0.6). GREEN target is
Dawn's real T2 writer: peak-dev 0.00 / rms 0.00, reproducible over 3 sync
runs. A synthetic per-notch-varying oracle drives 74/77 notches to exactly
STEP; the real writer re-pins in the same ResizeObserver cycle and closes
the synthetic-only outliers.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 15:01:05 -04:00
4f378a3cf4 L-E upscroll-jitter gate: RED metric isolating realization drift
Tyler's report: scrolling UP a fully-loaded channel is subtly jumpy;
DOWN is smooth. Root cause (Eva H1 + L-C): rows above the opening
viewport have never painted, so under content-visibility:auto they sit
at estimateRowHeight()'s contain-intrinsic-size reserve (60px,
utilities.css:17). Scrolling up, each realizes at its true height; the
reserve-vs-true delta shifts the reading position. Shipped WKWebView has
no overflow-anchor, so it corrects nothing — every delta lands raw.

This adds a deterministic gate that is RED at tip 77bd0e70 and that the
converged owned-compensation fix (overflow-anchor:none + same-frame
scrollBy(realized-reserved)) turns green:

- e2eBridge: a dedicated `jitter-corpus` mock channel (400 structurally
  heterogeneous rows — headings/lists/blockquotes/code fences/long prose
  whose true height estimateRowHeight is known to miss), its own channel
  so the uniform deep-history seed the load-older specs use is untouched.
- upscroll-jitter.perf.ts: forces overflow-anchor:none on the scroller
  (mirrors shipped WKWebView; logs CSS.supports per engine), then wheels
  up in notches tracking a row held INSIDE the viewport (SAFE_MARGIN band
  from both edges, re-picked each step, scored only if the same id stayed
  in-band before+after). Residual = row's visual delta-top minus the
  scroll delta: 0 == smooth. Gate is peak <=2px, rms <=0.6px.

Validity: within a single cold run the already-realized rows read
residual 0.0 exactly while realizing rows drift -27..-75px — an artifact
would drift on both. Negative sign shows the estimator over-reserves.
At tip: peak 75px, rms 40px over 23 steps → RED. A correct fill of
scrollTop absorbs the drift into the scroll delta, driving residual->0.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 15:01:05 -04:00
2cc0eb5396 perf(desktop): GUI performance sweep — async offload, poll reduction, render stabilization (#1641)
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6 <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1jmc9dt2lyvzu3h0kxlwxt5zg4fxp9476awyxw6gwxn72g6cw7exqs64whm <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
2026-07-08 14:58:27 -04:00
11608ef675 fix: user search returns zero results for partially typed names (#1603)
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 14:57:42 -04:00
577fc8df52 copy(desktop): finish the persona→agent rename in rendered UI (B4.1) (#1647)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
2026-07-08 11:07:29 -07:00
c9c868b250 Community moderation UI: integration branch (data layer + member surface base) (#1617)
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6 <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1jmc9dt2lyvzu3h0kxlwxt5zg4fxp9476awyxw6gwxn72g6cw7exqs64whm <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
2026-07-08 13:49:54 -04:00
863aeb79f3 Community moderation Phase 1: reports, bans/timeouts, audit, tombstones, relay-DM notices (#1616)
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6 <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1jmc9dt2lyvzu3h0kxlwxt5zg4fxp9476awyxw6gwxn72g6cw7exqs64whm <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
2026-07-08 13:36:46 -04:00
41b187f936 copy(desktop): the word "persona" leaves the UI (Phase 1B.4) (#1646)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
2026-07-08 17:33:26 +00:00
c145037aad feat(desktop): flapping bee on the setup loading screen (#1631)
Signed-off-by: Fizz <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: klopez4212 <klopez4212@gmail.com>
Co-authored-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 10:25:03 -07:00
Aaron GoldsmithandGitHub 3e982fd371 fix(desktop): make ⌘K open the composer link editor for selections (#1644)
Signed-off-by: Aaron Goldsmith <aarong@squareup.com>
2026-07-08 10:24:17 -07:00
Will PflegerandGitHub 563dab92e4 fix(linux): make AppImage work on Mesa 25+ / GLib 2.88 distros (#1567) 2026-07-08 13:18:48 -04:00
f968601ef6 fix(desktop): add unread dot to workspace rail for channel unreads (#1637)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-08 13:17:22 -04:00
9683ef8079 refactor(desktop): converge the three definition→instance mappings (Phase 1B.3.5) (#1645)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
2026-07-08 10:08:40 -07:00
Atish PatelandGitHub f929aa09e6 Hide non-invocable member agents from @-mention autocomplete (#1611) 2026-07-08 12:57:33 -04:00
fa816d5bfa fix(desktop): clamp HDR avatar images to SDR range (#1642)
Signed-off-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
2026-07-08 09:54:06 -07:00
aa17294ad0 refactor(desktop): definition-edit routes through AgentDialog — single dialog entry point (Phase 1B.3c) (#1643)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
2026-07-08 09:28:09 -07:00
79a0575342 fix(desktop): refresh persona env vars on respawn — record.env_vars is overrides-only (#1640)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
2026-07-08 08:58:45 -07:00
8af18ee325 refactor(desktop): re-host instance edit as AgentInstanceEditDialog behind AgentDialog (Phase 1B.3b) (#1639)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
2026-07-08 08:46:32 -07:00
01c6b8566e feat(desktop): show full URL tooltip on masked link hover (#1625)
Signed-off-by: Aaron Goldsmith <aargoldsmith@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 15:35:04 +00:00
f437e43d09 benchmarks: Harbor Terminal-Bench harness for Buzz agent-team orchestration (harbor-buzz-orchestra) (#1504)
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-08 11:32:11 -04:00
2f1cd67714 fix(desktop): paint Buzz gradient on the workspace rail (#1636)
Signed-off-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
2026-07-08 16:31:21 +01:00
48bd8abfeb refactor(desktop): single-home the definition form as AgentDefinitionDialog (Phase 1B.3a) (#1627)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
2026-07-08 07:56:42 -07:00
6f38db6803 feat(desktop): unified AgentDialog create entry point with create intents (Phase 1B.2) (#1626)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
2026-07-08 07:42:32 -07:00