fix(config): gate Instance dispose on actual config change#722
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request optimizes configuration updates in packages/opencode/src/config/config.ts by ensuring that disk writes and instance teardowns only occur when the configuration content has actually changed. By comparing the new configuration against the existing file content, the code avoids unnecessary interruptions to active processes caused by no-op writes. I have no feedback to provide as there were no review comments.
Port upstream PR #25114's `changed` byte-comparison gate to both
`Config.update` and `Config.updateGlobal`. Without it, a no-op global
or project config write tears down all running instances via
`Instance.disposeAll()`, which closes each SessionRunState scope mid-
turn and surfaces to the user as `MessageAbortedError` with a tool
showing `Tool execution aborted`.
PawWork's existing `update` path always called `Instance.dispose()`
unconditionally, and `updateGlobal` always called `invalidate()`
(which calls `Instance.disposeAll()`) unconditionally. Upstream
already fixed the same class of bug in opencode commit `a9d399699`
("Prevent Model response Interruption when opening settings dialog"),
but the fix was not in PawWork's `dev` history.
This is a candidate fix for #721. It does not change behavior when
the write genuinely changes bytes on disk. The first global write on
a fresh install still materializes the seeded file because the gate
also keys on `!fileExisted`.
Verification: bun --cwd packages/opencode run typecheck passes,
bun --cwd packages/opencode test test/config/pawwork-global-config.test.ts
test/config/config.test.ts passes 153/153.
Sentinel-mtime regression for the changed gate added in the previous commit. If the gate ever regresses, the second saveGlobal would rewrite the file and bump mtime past the sentinel, failing this test. Reviewer-requested guard so we explicitly assert the behavior rather than rely on the existing 153 config tests covering only functional correctness.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
1a476b6 to
ca2e368
Compare
|
On the P2 follow-up around cache potentially lagging disk after a no-op 1. The premise does not hold in PawWork. PawWork is a single-instance Electron app ( 2. Upstream evaluated the same trade-off and did not split. The fix we are porting (opencode commit 3. Splitting cache refresh from instance dispose is not a small change. Plan: keep #722 at its current minimal scope and merge. No follow-up issue. If PawWork later gains a multi-process write path, or if production logs show this race actually firing, we revisit then. |
Summary
Port upstream PR anomalyco/opencode#25114's
changedbyte-comparison gate to bothConfig.updateandConfig.updateGlobalso that a no-op config write does not tear down running instances.Why
Without the gate, both
Config.update(project config) andConfig.updateGlobal(global config) callInstance.dispose()/Instance.disposeAll()on every write. A write of identical bytes — e.g., a UI mount that re-emits the existing setting — still disposes every running instance, which closes eachSessionRunStatescope mid-turn. The user sees the active assistant turn end withMessageAbortedErrorand anTool execution abortedtool result, then has to resend.Upstream already fixed this same class of bug in opencode commit
a9d399699("Prevent Model response Interruption when opening settings dialog"), but the fix is not in PawWork'sdevhistory (git branch -a --contains a9d399699returns onlyremotes/upstream/dev). PawWork'sConfig.updateandConfig.updateGlobalare still in the pre-fix shape.This PR ports just the
changedgate, not the broader #25475 instance-lifecycle decoupling refactor. The minimal change is enough to remove the no-op-write trigger.Related Issue
Candidate mitigation for #721. Deliberately not phrased with a closing keyword because we have not yet confirmed via post-#710 abort diagnostics that the silent writer is what's tearing down scopes in the reported cases. This PR removes one known trigger of that class. The #721 diagnostic comments lay out what evidence would confirm or refute that this was the root cause.
Human Review Status
Pending. A human should make the final merge decision after reviewing the final diff and verification evidence.
Review Focus
The
!fileExistedclause inupdateGlobal. PawWork'supdateGlobalhas a seed-migration path: on a fresh install with no global config file, it seeds from upstream config sources. If we gated purely on byte equality, the seeded content would never materialize becauseserialized === beforeon first run. The!fileExistedguard makes sure we always write on first run even when the merged result equals the seed text. Worth confirming this is correct for the existing seed test cases.Risk Notes
Low behavior risk. The change only suppresses dispose when the write would have produced identical bytes. Any write that actually changes config still disposes as before. No public API change. No platform-specific concern.
How To Verify
Screenshots or Recordings
Not applicable. No visible UI change.
Checklist
bug,enhancement,task, ordocumentation), at least one primary routing label (app,ui,platform,harness, orci), and exactly one priority label (P0toP3), or I requested maintainer labelingdev, and my PR title and commit messages use Conventional Commits in English