Merge branch 'feat/local-model-library' into feat/local-model-harness-choice

# Conflicts:
#	src/web/CLAUDE.md
This commit is contained in:
Codeman maintainer committed 2026-11-12 00:11:10 +01:00
commit b724f2d6ea
5 files changed
+98 -10

No files matched your search

+24
View File
@@ -375,6 +375,30 @@ describe('sessionNamesUsingEndpoint', () => {
expect(sessionNamesUsingEndpoint(new Set(sessions), 'office-gpu')).toEqual(['w3-lib']);
expect(sessionNamesUsingEndpoint(new Map().values())).toEqual([]);
});
// Seen on a Mac: a worker the codeman skill started has no name, and the refusal read
// "Sessions are still using this model: . Close them first."
it('names a session without a name the way its tab does: the folder, else the short id', () => {
const ours = { endpointId: 'local-ollama' };
expect(
sessionNamesUsingEndpoint([
{
id: '7dbd7b0d-0000-4000-8000-000000000001',
name: '',
workingDir: '/Users/x/codeman-cases/summaries',
customModel: ours,
},
{
id: 'a030dddb-0000-4000-8000-000000000002',
name: ' ',
workingDir: '/Users/x/codeman-cases/review/',
customModel: ours,
},
{ id: 'c1d2e3f4-0000-4000-8000-000000000003', name: '', workingDir: '', customModel: ours },
{ id: 'ffffffff-0000-4000-8000-000000000004', name: 'w1-app', workingDir: '/Users/x/app', customModel: ours },
])
).toEqual(['summaries', 'review', 'c1d2e3f4', 'w1-app']);
});
});
describe('compareModelDigest', () => {
+43
View File
@@ -2681,6 +2681,49 @@ describe('the models on this Mac', () => {
expect(buttons(page.body)).toEqual(['Start a session']);
});
// Seen on a Mac: Use, then the list was gone, and deleting the other model meant closing the
// window and opening it again.
it('a switch that ends in the open window comes back to the list, the other model in use', async () => {
const page = mount();
const switching = status(
{},
{
...TWO.install,
state: 'installing',
step: 'verify',
steps: ['verify', 'connect'],
model: { id: 'gpt-oss:20b', label: 'GPT-OSS 20B' },
library: [
{ ...QWEN_ROW, active: false },
{ ...GPT_ROW, actions: [] },
],
actions: ['cancel'],
}
);
const switched = status(
{},
{
...TWO.install,
model: { id: 'gpt-oss:20b', label: 'GPT-OSS 20B' },
library: [
{ ...GPT_ROW, active: true, actions: [] },
{ ...QWEN_ROW, active: false, actions: ['use', 'delete'] },
],
}
);
const { app, serve } = await opened(page, switching);
expect(page.body.dataset.screen).toBe('progress');
serve(switched);
await app.loadLocalModel({ force: true });
expect(page.body.dataset.screen).toBe('installed');
expect(texts(page.body, '.lm-lib-label')).toEqual(['GPT-OSS 20B', 'Qwen3.6 35B-A3B']);
expect(texts(page.body, '.lm-lib-tag .lm-tag')).toEqual(['In use', 'Ready']);
expect(rowButtons(page)).toEqual(['Use', 'Delete']);
// The whole installed screen, not the one-button screen that ends an install.
expect(buttons(page.body)).toEqual(['Remove', 'Stop', 'Start a session']);
expect(texts(page.body, '.lm-lib-foot button')).toEqual(['Add a model']);
});
it('"Add a model" opens the chooser on an installed model, and Back returns to it', async () => {
const page = mount();
const { app } = await opened(page, TWO);