Skip to content

openwork-server: no-op provider patch still reloads the engine (#3307 guard ineffective in production) #3321

Description

@benjaminshafii

#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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions