fix(local-model): a view read while the job ends no longer shows paused

The install window polls GET /api/local-model every second. A read of the
state file that was still out while the job wrote its last state and let
go of this.job came back with the old `installing`, and settling that with
no job in the process read it as an interrupted job: `paused` with Resume
for one tick before `failed`. Under CI load this made
test/local-model-install-e2e.test.ts fail on a different failure case per
run (master 86502b69, PR #790).

currentState() now reads again when the job ended during the read. The job
writes before it lets go, so the second read has the final state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-09 02:51:07 +01:00
1 parent 86502b6904
commit f62c1f7e2f
2 files changed
+60 -2

No files matched your search

+9 -2
View File
@@ -1053,9 +1053,16 @@ class LocalModelServiceImpl implements LocalModelService {
return progress ? { ...next, progress } : next;
}
/** The stored state as a view or a verb must see it: settled, unless a verb is just starting a job. */
/**
* The stored state as a view or a verb must see it: settled, unless a verb is just starting a
* job. A job writes its last state BEFORE it lets go of `this.job`, so when the job ended while
* this read was out, the read may hold the `installing` from before that write; it is read
* again rather than settled, or it would show as an interrupted (`paused`) job for a tick.
*/
private async currentState(): Promise<LocalModelStateFile> {
const state = await this.deps.readState();
const jobAtRead = this.job;
let state = await this.deps.readState();
if (jobAtRead && !this.job) state = await this.deps.readState();
return this.jobStarting ? state : this.settled(state);
}