Commit Graph
10 Commits
Author SHA1 Message Date
37547a7bd1 Release exp-v0.52.171: keep settled Worklog in place across stream settlement (#6454) (#6837)
* test: prove worklog continuity across stream settle

* fix: keep settled worklog in place on stream done

* fix: preserve settle follow ordering

* test: wire settled worklog frame proof

* fix: preserve settled worklog detail disclosure

* fix: preserve default-open settled worklogs

* fix: preserve blank-owner worklogs

* fix: keep render fallback self-contained

* ci: retrigger hosted checks

* style(worklog): grow collapsed Worklog rail tap target to 44px on touch devices (#6454 follow-up)

* Release exp-v0.52.171: keep settled Worklog in place across stream settlement (#6454)

---------

Co-authored-by: Frank Song <franksong2702@gmail.com>
Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
2026-08-07 18:06:57 -07:00
Frank Song cbe353fb93 Scope preserveDom to live assistant turn only 2026-07-24 13:43:15 +08:00
Frank Song ae264f71a3 Preserve live Worklog through final settlement 2026-07-24 12:43:34 +08:00
webtecnica f35081683a fix tests for #6385 scroll snapshot: update #4970 assertion + add behavioral coverage
- Fix test_stream_done_runs_scroll_preserving_collapse_pass_after_disarm:
  assert _doneLiveScrollSnapshot is captured before arm, and the
  argument-bearing _renderMessagesWithScrollSnapshot({_prescrollSnapshot:_doneLiveScrollSnapshot})
  call is used after disarm (the no-arg literal no longer exists).
- Add test_prescroll_snapshot_bypasses_capture_no_option_fallback_still_captures:
  Node.js behavioral harness that supplies a sentinel _prescrollSnapshot,
  stubs _captureMessageScrollSnapshot with a counter, and proves:
    1) sentinel reaches _restoreMessageScrollSnapshotSameFrame without a capture
    2) no-option / empty-{} calls still capture normally (callers at
       static/ui.js:9565, 14416, 14424).
2026-07-21 19:55:00 -03:00
0037061cfd Release exp-v0.52.45: errored-turn response visible (#5941) + recovery honors model pick (#5924) (#5964)
* fix(streaming): keep errored-turn assistant response visible (#5941)

An errored turn that produced assistant content (tool calls + reasoning)
folded that content into a collapsed worklog above the error card, so the
user read a lone error bubble as "nothing came back". The settled-scene
renderer collapsed the worklog unconditionally, never consulting the
turn terminal_state. Now an errored/failure terminal_state keeps the
worklog expanded by default (unless the user explicitly collapsed it),
while completed turns and genuinely-empty errored turns are unchanged.

Reported by @b3nw.

* fix(review): update #4970 brittle collapsed-expr assertion for #5941

test_issue4970_stream_done_shrink_regression asserted the literal
`collapsed:!keepSettledWorklogOpen` in _renderSettledAnchorSceneForMessage.
#5941 OR'd an errored-turn keep-open term into that expression
(`collapsed:!(keepSettledWorklogOpen||erroredWorklogKeepOpen)`), so the
substring no longer matched and the test failed in the full suite (the PR
updated its own touched tests but missed this pre-existing one).

Re-anchor to `collapsed:!(keepSettledWorklogOpen` — still guards #4970's
invariant (keepSettledWorklogOpen negates into the settled-render collapsed
flag) without pinning the exact term list. #4970 + #5941 suites green.

* fix(chat): honor explicit model pick on post-failure recovery send (#5924)

The onchange explicit-pick marker is single-shot: send() consumes it once.
submitEdit() and cmdRetry() truncated and called send() directly without
re-arming it, so the recovery send went out with explicit_model_pick=false and
the server's compatible-model resolution re-reverted a freshly-picked
cross-family model back to the failed/stale value (Facet 1). Facet 4 is the
same loop keeping the persisted model_provider pinned across fork/refresh.

Re-arm the pending explicit-pick marker from the current selector state
immediately before await send() in both recovery paths. Survives a second
consecutive recovery send. Normal send path and the #3737/#5731 server repair
guard are unchanged.

Reported by @b3nw.

* fix(#5924): gate recovery re-arm on genuine pick + session-race guards

Codex gate found 4 defects on the recovery send path; fixed all:
- /retry + edit-resubmit re-armed the explicit-pick marker UNCONDITIONALLY, forcing
  explicit_model_pick even with no fresh pick (suppressed server compatible-model
  resolution). Now gated on _recoveryPick (selector model differs from session's own
  stored model), captured pre-await.
- session-switch races: added active-session guards after the retry GET await and the
  edit truncate await so session A's recovery intent can't apply to session B.
- 3 new regression tests (gated re-arm, pre-await capture, post-await re-guard).

* fix(#5924) round-2: derive recovery pick from non-default session model, not inference

Codex round-2 CORE: the state-comparison predicate false-negatived an already-applied
pick (consumed marker → looks unchanged) and false-positived on provider inference
(@removed:mistral-large + null stored provider → inferred 'removed' → fake change).
Replace with _deliberateSessionModelPick(sid): reports {model,provider} only when the
session's OWN model is genuinely non-default vs window._defaultModel/_activeProvider —
inference-free + survives marker consumption. Both recovery paths + tests updated.

* fix(#5924) round-3: require known-default+owned-provider evidence + fire-time re-arm guard

Codex round-3: (1) _deliberateSessionModelPick still false-positived when the
profile default was unknown or the provider was only inferred — now requires a
session-OWNED provider AND a known window._defaultModel/_activeProvider, else
fails closed. (2) new same-session race: a model change DURING the recovery awaits
made the pre-await pick stale and clobbered a newer marker — new _reArmRecoveryPick
helper re-arms only if the current session model/provider still equals the captured
pick AND no different pending marker exists. Both recovery paths route through it.

* Release exp-v0.52.45: errored-turn response stays visible (#5950/#5941) + recovery honors model pick (#5949/#5924)

* fix: keep absoluteKeepCount capture before _recoveryPick in submitEdit

The #5924 _recoveryPick comment contained the word 'await', tripping the
pre-existing test_issue_edit_regenerate_absolute_keep_count regex (first \bawait\b
must come after the absoluteKeepCount capture). Reorder both synchronous pre-network
captures (absoluteKeepCount first) + reword the comment. No behavior change.

---------

Co-authored-by: nesquena-hermes <agent@nesquena-hermes>
Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
2026-07-11 18:07:28 -07:00
allenliang2022 0a1cf564c7 fix(chat): make keep-settled-worklog-open genuinely transient (#5260 gate-cert)
The RED gate-cert (Codex-verified) found the keep-open was NOT the one-frame
transient the PR claimed: _disarmKeepSettledWorklogOpen() only cleared the token
with no follow-up render, so the just-settled worklog stayed force-open and could
be cached into _sessionHtmlCache and persist across session restores.

Two coordinated fixes:

1. Don't cache the forced-open DOM. Add _isKeepSettledWorklogOpenArmed() and gate
   the _sessionHtmlCache.set() population on a typeof-safe !_keepOpenArmed local, so
   the armed settle render is never cached. The typeof guard also keeps standalone
   renderMessages() node harnesses (which don't define the helper) working.

2. Collapse pass after disarm for BOTH pin states. The STREAM_DONE handler disarms,
   then runs _renderMessagesWithScrollSnapshot() UNCONDITIONALLY (a scroll-preserving
   re-render that collapses the worklog back to its copied live/user disclosure
   state), and only THEN scrollToBottom() for followers to re-settle at the tail.
   The second RED gate-cert caught that gating the collapse pass to the non-following
   path left a pinned follower forced-open, because scrollToBottom() only settles
   scroll position and does NOT re-render. Running the collapse render before the
   follower re-settle fixes both pin states. The same-frame JS restore absorbs the
   collapse height change instead of leaving it to native scroll-anchoring (which
   mobile suppresses mid-render), so there is no jump, and this unarmed render is
   the one that populates the cache.

Net: keep-open is genuinely one settle render for pinned AND unpinned readers —
neither persists nor caches — and a user-collapsed worklog is preserved. Verified
e2e on an isolated debug instance for both pin states (worklog open-while-armed ->
collapsed-after-pass; pinned follower stays pinned at tail). Regression tests lock
the cache-skip and the collapse-before-scrollToBottom ordering; the scrollToBottom
adjacency keeps the #1360 done-follow test green.
2026-06-30 17:06:31 +00:00
allenliang2022 d9622bf9b0 fix(chat): keep just-settled worklog open for unpinned readers too (mobile 往回大跳)
The STREAM_DONE keep-open exception (#4970 round 6/7) was scoped to PINNED
followers only, on the assumption that unpinned readers 'preserve their viewport
normally'. That holds on desktop (overflow-anchor:none + JS snapshot restore) but
NOT on mobile.

Repro (isolated debug instance + CDP touch emulation, faithful to a real phone):
a reader who scrolls UP to read inside the just-settled turn is UNPINNED. At
STREAM_DONE the live worklog/thinking cards collapse into a compact summary,
shrinking the transcript by hundreds of px ABOVE their viewport. On mobile the
CSS resting value is overflow-anchor:auto, but _fixMobileScrollJank() flips an
inline overflow-anchor:none over the settle render — so native scroll-anchoring
is suppressed during exactly the frame the unpinned reader needs it to absorb the
above-viewport shrink. The content then leaps to the top of the latest turn (the
'往回大跳' report). Single-variable measurement: collapse-with-auto shifts the
reader 0px; collapse-with-none shifts -248px.

Fix: keep the just-settled worklog open for BOTH pin states (still one-shot
scoped to the turn that just settled, so historical worklogs still collapse
compact). This removes the shrink entirely, fixing the jump for every
device/anchor-mode instead of fighting the anchor engine. The #4856 guard and the
mobile overflow-anchor:auto CSS are untouched, so Boev's Android DOM-wipe fix and
akrhin's #MOBILESCROLL fix both stay intact.

- Rename _shouldKeepSettledWorklogOpenForPinnedFollow ->
  _shouldKeepSettledWorklogOpenForStreamSettle (drops the _scrollPinned /
  _messageUserUnpinned gate; keeps the one-shot stream-id token).
- Update the #4970 behavioral regression test to assert keep-open is TRUE for the
  armed turn under BOTH pin states, FALSE for historical turns and after disarm.
2026-06-30 17:06:31 +00:00
allenliang2022 2aa1d168b0 fix(chat): scope STREAM_DONE keep-open to just-settled turn via one-shot token
Address maintainer must-fix: _shouldKeepSettledWorklogOpenForPinnedFollow()
keyed only on global pin state, so every historical settled worklog re-expanded
on any pinned re-render — defeating the compact default. Gate the keep-open
exception on a one-shot token = the just-settled stream id (armed at STREAM_DONE
renderMessages, disarmed right after), threaded as _shouldKeep...(streamId).
Unpinned path unchanged. Replace source-string test with a behavioral Node test
that drives a second pinned turn and asserts it collapses.
2026-06-29 17:06:44 +00:00
allenliang2022 0d198d677d test(chat): document keep-open precedence over live collapse state
Follow-up to maintainer review on #5058: make the pinned-follower helper comment
explicit that avoiding the visible STREAM_DONE shrink jump intentionally takes
precedence over a transient user-collapsed live worklog while the reader remains
pinned. Add a source lock so that intent stays documented.

Focused scroll locks remain green (22 passed), node --check clean.
2026-06-29 17:06:44 +00:00
allenliang2022 5fd3b1c9d7 fix(chat): prevent stream-end worklog collapse jump for pinned readers
A new post-#4970 scroll-jump class surfaced after Release YH: when a streamed
assistant turn with a large live worklog/tool trace settles, the settled compact
worklog collapses hundreds of pixels at STREAM_DONE. The reader is still pinned
and scroll state is correct, but the net scrollHeight shrink makes the browser
clamp scrollTop down by the same amount, which is visible as a large backward
jump.

Fix by keeping the just-settled activity worklog open for pinned followers
(_scrollPinned && !_messageUserUnpinned), so the live->settled DOM swap is
height-stable. Unpinned readers still get compact settled worklogs and keep their
viewport normally. Also make _anchorSceneWorklogGroup respect opts.collapsed;
previously it hard-coded collapsed: !live so the caller could not request an open
settled group.

Regression locks assert the pinned-follow helper, that settled rendering passes
collapsed:!keepSettledWorklogOpen, and that _anchorSceneWorklogGroup honors an
explicit opts.collapsed. Verified locally with a Playwright repro: before the
fix a large tool-worklog answer produced scrollHeight -367/-422px and scrollTop
-367/-422px at STREAM_DONE; after the fix nShrinks=0 and nBack=0. Local focused
scroll locks pass: 22 passed.
2026-06-29 17:06:44 +00:00