Phase 1 (#4662): address Codex + Opus gate findings

Codex (CORE regression): the parallelized loadDir('.') started BEFORE the
_switchGen stale-switch guard; empty-session switches reuse the session id, so a
superseded switch's /api/list could paint the wrong profile's tree. Reverted to
guard-before-workspace-render ordering (the up-front skeletons already give the
immediate cross-surface feedback; the parallel gain was marginal).

Opus findings:
- [blocker] Workspace skeleton stranded permanently when the new profile has no
  bound workspace (loadDir skipped → nothing clears #fileTree). Added
  clearWorkspaceTreeSkeleton() (only empties when a skeleton is present) and call
  it on the no-workspace path in BOTH switch branches.
- [low-med] Session-list skeleton stranded if an inline-rename input or row
  action menu was open (renderSessionListFromCache early-returns before the DOM
  swap). Dismiss _renamingSid + closeSessionActionMenu() before showing the
  skeleton.
- [low] No-op self-switch flashed skeleton→restore (activateCurrentProfile
  doesn't pre-check). Added an early-return when name === S.activeProfile.

Tests: +4 static assertions for the self-switch guard, rename/menu dismissal,
no-workspace skeleton clear, and the clear-helper definition. 22 pass.
This commit is contained in:
nesquena-hermes authored and nesquena-hermes committed 2026-06-22 18:12:25 +00:00
1 parent 4abdb54ede
commit adcd06ff76
3 files changed
+97 -14

No files matched your search

+12
View File
@@ -475,6 +475,18 @@ function showWorkspaceTreeSkeleton(){
tree.style.display = '';
}
// Clear a stranded workspace-tree skeleton (#4662 Opus gate). showWorkspaceTreeSkeleton()
// is shown up front on a profile switch, but the real loadDir('.') that would
// replace it is skipped when the new profile has no bound workspace — leaving a
// shimmering skeleton forever. Call this on the no-workspace path so the tree
// empties instead. Only touches #fileTree when it still holds a skeleton, so
// it can't clobber a real render.
function clearWorkspaceTreeSkeleton(){
const tree = $('fileTree');
if(!tree) return;
if(tree.querySelector('.skeleton-tree')) tree.innerHTML = '';
}
async function loadDir(path, opts={}){
const preservePreview=!!(opts&&opts.preservePreview);
const refreshExpanded=!!(opts&&opts.refreshExpanded);