fix(release): the seven findings from the pre-release review of the whole tree

A full review of the release tree found seven things, and four of them were mine.

**The gate was red, and I put it there.** Splitting `confirmed` into `confirmedContext`
and `confirmedSwap` changed the wire field without moving three assertions that check
it: `custom-model-one-shot-launch.test.ts` and two in `custom-model-run-menu-ui.test.ts`
(the swap modal and the context modal, each of which already receives exactly the right
per-question flag). Moved, with the titles.

**Worse, my own tests for the split never ran.** The four cases in
`session-custom-model.test.ts` that exist specifically to pin it call `mockRunning()`,
which was declared inside a sibling `describe`, so they threw a ReferenceError during
setup. The split would have shipped with no passing server-side coverage while the gate
reported the failure as four broken tests rather than as four tests that were never
written. `mockRunning` is hoisted to the outer describe.

**The submit verifier pressed Enter into shell panes.** `#455`'s SubmitVerifier resolved
its composer glyph as `promptGlyph ?? '❯'`, and only claude and codex declare one, so
the other eight modes fell back to claude's `❯`. That is also starship's default shell
prompt, and pure's, and spaceship's, and p10k lean's. On such a shell the line
`❯ npm run build` sits on screen for as long as the command runs, the verifier reads it
as an unsubmitted prompt, and re-presses Enter into the running program's stdin up to
nine times on its 2s..60s schedule. Mostly a stray newline; not harmless against a y/N
prompt, `read -p`, an installer or a pager, where it takes the default. The module's own
fileoverview already stated the rule this broke. Now `?? ''`, which
`promptStillInComposer()` already treats as inert, so the verifier runs only for a CLI
that actually declares a composer.

**My #451 dedent removal left a count behind**: "Two rules keep it honest" introducing
three numbered rules.

The rest is documentation the split outran. `confirmedContext`/`confirmedSwap` appeared
in no doc at all, while `docs/api-reference.md` (the SemVer-covered contract) still told
an integrator to retry with `confirmed: true` for both questions, which is precisely the
thing the split exists to stop. Documented there, in `docs/custom-model-endpoints.md`
and in CLAUDE.md. The custom-model changeset gained the split and the `CLAUDE_CONFIG_DIR`
multi-user consequence, both user-visible and both previously absent, and #454's gained
the one exception to its own claim: a Custom Endpoints launch ignores the Instance count
stepper and always starts one session.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-09-19 12:58:33 +02:00
parent 12de3c5164
commit 4205f6930f
10 changed files with 65 additions and 23 deletions
+5 -2
View File
@@ -154,7 +154,7 @@ describe('_quickStartWithCustomModelConfirm', () => {
expect(app._lastCustomModelLaunchResult).toEqual(data.data);
});
it('confirming re-sends with confirmed:true and returns the second response', async () => {
it('confirming re-sends with confirmedSwap and returns the second response', async () => {
const { win, app } = bootApp();
app._confirmModelSwap = async () => true;
let calls = 0;
@@ -170,7 +170,10 @@ describe('_quickStartWithCustomModelConfirm', () => {
},
};
}
expect(body.customModel.confirmed).toBe(true);
// the SWAP question's own flag, never the blanket `confirmed`: answering this one
// must not also silence the context-floor warning.
expect(body.customModel.confirmedSwap).toBe(true);
expect(body.customModel.confirmed).toBeUndefined();
return { success: true, data: { sessionId: 's1', modelSwapInProgress: true } };
});
const data = await app._quickStartWithCustomModelConfirm({
+2 -2
View File
@@ -574,7 +574,7 @@ describe('Custom Model Endpoint Profiles: llama-swap model-swap confirmation and
expect(confirmMessage).toContain('qwen3');
expect(applyBodies).toEqual([
{ endpointId: 'llama-box', modelId: 'qwen3' },
{ endpointId: 'llama-box', modelId: 'qwen3', confirmed: true },
{ endpointId: 'llama-box', modelId: 'qwen3', confirmedSwap: true },
]);
});
@@ -1041,7 +1041,7 @@ describe("Custom Model Endpoint Profiles: requiresContextWarning (this CLI's own
expect(confirmArgs).toEqual(['qwen3', 16384, 40000]);
expect(applyBodies).toEqual([
{ endpointId: 'llama-box', modelId: 'qwen3' },
{ endpointId: 'llama-box', modelId: 'qwen3', confirmed: true },
{ endpointId: 'llama-box', modelId: 'qwen3', confirmedContext: true },
]);
});
+11 -7
View File
@@ -39,6 +39,17 @@ async function setup(ctxOptions?: Parameters<typeof createRouteTestHarness>[1])
}
describe('POST /api/sessions/:id/custom-model', () => {
/** Shared by the conflict-check block and the context-floor block below, which needs
* both conditions true at once. Scoped to the outer describe on purpose: while it
* lived inside the conflict-check block, a sibling calling it threw a ReferenceError
* during setup, so those tests reported as failing rather than as not written. */
function mockRunning(running: Array<{ model: string; state: string }>) {
fetchMock.mockImplementation(async (url: URL) => {
if (url.pathname === '/running') return new Response(JSON.stringify({ running }), { status: 200 });
throw new Error(`unexpected request in this test: ${url.href}`);
});
}
beforeEach(async () => {
await writeCustomModelHosts(getDataDir(), []);
fetchMock.mockReset();
@@ -229,13 +240,6 @@ describe('POST /api/sessions/:id/custom-model', () => {
});
describe('llama-swap conflict check (llama.cpp runs one model at a time)', () => {
function mockRunning(running: Array<{ model: string; state: string }>) {
fetchMock.mockImplementation(async (url: URL) => {
if (url.pathname === '/running') return new Response(JSON.stringify({ running }), { status: 200 });
throw new Error(`unexpected request in this test: ${url.href}`);
});
}
it('applies straight away when the requested model is already loaded', async () => {
const { app, ctx } = await setup();
ctx.sessions.get('test-session-1')!.mode = 'claude';