fix(#5501): clear pending retry state on render invalidation (Codex gate)

Codex re-gate SILENT: a retry whose fetch is invalidated mid-flight (e.g. a
profile switch) left the button stuck as an inert 'Retrying…' with no request in
flight — the stale catch returns before _showSessionListLoadError and the
.finally() bails when the skeleton removed the old button. _invalidateSessionListRenders()
now clears the pending retry markers (retrying, _retryFailedFocus) so the next
repaint shows an actionable idle Retry. Regression test added (fails without the
fix); test harness extracts _invalidateSessionListRenders + declares its render-gen
globals.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
This commit is contained in:
nesquena-hermesandrodboev committed 2026-07-05 04:48:50 +00:00
1 parent 9be4048c72
commit f6cc48b834
2 files changed
+51

No files matched your search

+11
View File
@@ -4251,6 +4251,17 @@ function _invalidateSessionListRenders(){
_renderSessionListGen++;
_pendingSessionListPayload = null;
_renderSessionListQueuedRequest = null;
// A retry whose fetch is invalidated here (e.g. a profile switch mid-retry)
// would otherwise leave the error note stuck as an inert "Retrying…" button
// with no request in flight — the stale fetch returns before
// _showSessionListLoadError and the .finally() bails when the old button was
// removed. Clear the pending retry markers so the next repaint shows an
// actionable idle Retry again.
if(_sessionListLoadError && (_sessionListLoadError.retrying || _sessionListLoadError._retryFailedFocus)){
_sessionListLoadError = {..._sessionListLoadError};
delete _sessionListLoadError.retrying;
delete _sessionListLoadError._retryFailedFocus;
}
}
if(typeof window!=='undefined') window._invalidateSessionListRenders = _invalidateSessionListRenders;
@@ -39,6 +39,7 @@ def _build_node_script(extra_js):
src = SESSIONS_JS.read_text(encoding="utf-8")
show_error_fn = _extract_function(src, "_showSessionListLoadError")
retry_note_fn = _extract_function(src, "_renderSessionListLoadErrorNote")
invalidate_fn = _extract_function(src, "_invalidateSessionListRenders")
return f"""
class FakeClassList {{
constructor(node) {{
@@ -146,6 +147,9 @@ global.document = {{ createElement: (tag) => new FakeElement(tag) }};
global.$ = (id) => id === 'sessionList' ? list : null;
global._allSessions = [];
global._sessionListLoadError = null;
global._renderSessionListGen = 0;
global._pendingSessionListPayload = null;
global._renderSessionListQueuedRequest = null;
global._sessionListFromCacheCalls = [];
global.renderSessionListCalls = [];
global.renderSessionListFromCache = () => {{
@@ -184,6 +188,7 @@ global.renderSessionList = (opts) => {{
}};
{show_error_fn}
{retry_note_fn}
{invalidate_fn}
{extra_js}
"""
@@ -579,3 +584,38 @@ def test_session_list_error_note_has_live_region_and_pending_uses_aria_disabled(
assert "Retrying..." not in note_fn
# keyboard focus restored on a failed retry
assert "_retryFailedFocus" in note_fn
def test_retry_pending_state_cleared_on_render_invalidation():
"""#5505 gate (Codex SILENT): a retry whose fetch is invalidated mid-flight
(e.g. a profile switch) must not leave the button stuck as an inert
'Retrying…' with no request in flight. _invalidateSessionListRenders() clears
the pending retry markers so the next repaint shows an actionable idle Retry."""
if NODE is None:
pytest.skip("node not on PATH")
script = _render_script(
"""
_showSessionListLoadError(new Error('backend busy'));
global._sessionListLoadError = {...global._sessionListLoadError, retrying: true, _retryFailedFocus: true};
_invalidateSessionListRenders();
global.renderSessionListFromCache();
const note = list.children[0];
const retry = findButton(note);
console.log(JSON.stringify({
retryingCleared: !global._sessionListLoadError.retrying,
focusFlagCleared: !global._sessionListLoadError._retryFailedFocus,
text: retry.textContent,
ariaDisabled: retry.getAttribute('aria-disabled'),
clickable: typeof retry.onclick === 'function',
}));
"""
)
body = _run_node(script)
assert body == {
"retryingCleared": True,
"focusFlagCleared": True,
"text": "Retry",
"ariaDisabled": None,
"clickable": True,
}