feat: Sprint 23 -- profile/workspace/model coherence

Fix five coherence bugs in profile switching:
1. Model picker ignored profile default (localStorage stale key)
2. Workspace list was global (not profile-scoped)
3. DEFAULT_WORKSPACE was a boot-time singleton
4. Session list showed all profiles (no filtering)
5. switchToProfile() didn't refresh workspaces or sessions

Backend: workspace storage is now profile-local for named profiles,
switch_profile() returns default_model and default_workspace.
Frontend: switchToProfile() clears stale model pref, refreshes
workspace list and session list, sessions.js filters by active profile
with 'Show N from other profiles' toggle.

8 new tests. 400 pass / 23 fail (identical to baseline).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nathan Esquenazi
2026-04-03 11:46:15 -07:00
parent 0480bbf34c
commit 3520fa5643
8 changed files with 359 additions and 17 deletions

View File

@@ -663,14 +663,36 @@ async function switchToProfile(name) {
try {
const data = await api('/api/profile/switch', { method: 'POST', body: JSON.stringify({ name }) });
S.activeProfile = data.active || name;
syncTopbar();
// Refresh dependent panels
// Clear stale model pref so profile default applies
localStorage.removeItem('hermes-webui-model');
// Refresh model dropdown (profile may have different provider/models)
_skillsData = null;
await populateModelDropdown();
// Apply profile's default model if provided
if (data.default_model && $('modelSelect')) {
$('modelSelect').value = data.default_model;
if ($('modelSelect').value !== data.default_model) {
// Model not in list — add it
const opt = document.createElement('option');
opt.value = data.default_model;
opt.textContent = data.default_model.split('/').pop();
$('modelSelect').insertBefore(opt, $('modelSelect').firstChild);
$('modelSelect').value = data.default_model;
}
}
// Refresh workspace list (now profile-local)
_workspaceList = null;
await loadWorkspaceList();
// Reset profile filter and refresh session list
_showAllProfiles = false;
await renderSessionList();
syncTopbar();
// Refresh visible sidebar panels
if (_currentPanel === 'skills') await loadSkills();
if (_currentPanel === 'memory') await loadMemory();
if (_currentPanel === 'tasks') await loadCrons();
if (_currentPanel === 'profiles') await loadProfilesPanel();
if (_currentPanel === 'workspaces') await loadWorkspacesPanel();
showToast('Switched to profile: ' + name);
} catch (e) { showToast('Switch failed: ' + e.message); }
}