Problem
Config.saveConfigEffect (src/node/config/index.ts) logs write failures and swallows them (Effect.catch → log.error("Error saving config:")). As a result, editConfig() resolves even when nothing reached disk.
The config snapshot is cleared only after a successful write. readConfigOrDefault() caches the object it passes to the edit transform, so after a failed write every in-process reader sees the edited value, but config.json still holds the old one. The divergence becomes visible only after a restart, when the edit silently reverts.
This affects every config edit. One example: the unrelated-messaging consent toggle (setUnrelatedWorkspaceConsent), and the creation-time default grant added in #4440. Neither can detect the failure by re-reading, because the re-read returns the same cached object.
Suggested direction
Pick one:
- Surface write failures from
editConfig.
- Drop the snapshot on a failed save so the next read reflects disk.
Then let security-relevant callers fail closed. This needs its own design pass because it touches every config writer.
Found during review of #4440 (Codex thread on grantCreationUnrelatedWorkspaceConsent); deferred there as pre-existing.
Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high
Problem
Config.saveConfigEffect(src/node/config/index.ts) logs write failures and swallows them (Effect.catch→log.error("Error saving config:")). As a result,editConfig()resolves even when nothing reached disk.The config snapshot is cleared only after a successful write.
readConfigOrDefault()caches the object it passes to the edit transform, so after a failed write every in-process reader sees the edited value, butconfig.jsonstill holds the old one. The divergence becomes visible only after a restart, when the edit silently reverts.This affects every config edit. One example: the unrelated-messaging consent toggle (
setUnrelatedWorkspaceConsent), and the creation-time default grant added in #4440. Neither can detect the failure by re-reading, because the re-read returns the same cached object.Suggested direction
Pick one:
editConfig.Then let security-relevant callers fail closed. This needs its own design pass because it touches every config writer.
Found during review of #4440 (Codex thread on
grantCreationUnrelatedWorkspaceConsent); deferred there as pre-existing.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high