#3307 added a guard so an identical PATCH /runtime-config/providers skips the engine reload. Measured on the real openwork-0.18.11 image, it does not work.
Evidence (live sandbox, server 0.18.11)
before: disposals=3 cfg=1785363765841/4960 sha=1b1e31152dbf
identical PATCH: changed=false reload="reloaded"
after: disposals=4 cfg=1785363796180/4960 sha=1b1e31152dbf
- The config file is byte-identical before and after (
sha=1b1e31152dbf), only mtime moved.
changed=false, so the DB row was unchanged — therefore fileResult.changed must be returning true.
- The engine disposed anyway (
disposals 3 -> 4), which is what aborts in-flight agent runs.
The running binary really does contain the #3307 code, so this is not a stale image:
shouldReload = result.changed || fileResult.changed;
if (shouldReload) { await reloadOpencodeEngine(...) }
reload: shouldReload ? "reloaded" : "skipped"
Where to look
writeOpenworkRuntimeConfigFile in apps/server/src/openwork-runtime-config.ts compares await readFile(path, "utf8") against await buildOpenworkRuntimeConfig(...) and returns changed: false when equal. That comparison is evidently not matching even though the bytes it writes equal the bytes already on disk. Candidate causes not yet excluded: the compared serialization differing from the written one, a second writer alternating content between samples, or the write queue racing itself (fileWriteQueue).
Its unit tests pass, so a regression test must reproduce this against a real file, not a mocked FS.
Severity
Defence-in-depth only. #3301 stopped den-api sending redundant patches, which is what caused the production incident (991 engine disposals, agent runs aborted mid-tool-call, SSE dropped). This guard is the backstop so no future caller can cause it again.
Repro
With a host token inside a cloud sandbox: GET /runtime-config/providers, then PATCH that exact payload back, and watch grep -c 'disposing instance' in ~/.local/share/opencode/log/opencode.log plus the sha of /tmp/openwork-data/runtime-opencode-config.json.
#3307 added a guard so an identical
PATCH /runtime-config/providersskips the engine reload. Measured on the realopenwork-0.18.11image, it does not work.Evidence (live sandbox, server 0.18.11)
sha=1b1e31152dbf), only mtime moved.changed=false, so the DB row was unchanged — thereforefileResult.changedmust be returningtrue.disposals 3 -> 4), which is what aborts in-flight agent runs.The running binary really does contain the #3307 code, so this is not a stale image:
Where to look
writeOpenworkRuntimeConfigFileinapps/server/src/openwork-runtime-config.tscomparesawait readFile(path, "utf8")againstawait buildOpenworkRuntimeConfig(...)and returnschanged: falsewhen equal. That comparison is evidently not matching even though the bytes it writes equal the bytes already on disk. Candidate causes not yet excluded: the compared serialization differing from the written one, a second writer alternating content between samples, or the write queue racing itself (fileWriteQueue).Its unit tests pass, so a regression test must reproduce this against a real file, not a mocked FS.
Severity
Defence-in-depth only. #3301 stopped den-api sending redundant patches, which is what caused the production incident (991 engine disposals, agent runs aborted mid-tool-call, SSE dropped). This guard is the backstop so no future caller can cause it again.
Repro
With a host token inside a cloud sandbox:
GET /runtime-config/providers, thenPATCHthat exact payload back, and watchgrep -c 'disposing instance'in~/.local/share/opencode/log/opencode.logplus the sha of/tmp/openwork-data/runtime-opencode-config.json.