Client or integration
Other
Area
Other
Summary
The OpenCode (and any non-omp) client integration dead-ends in a permanent conflict state as soon as the user makes any edit to their own config file — even edits that never touch the opencodex-owned block. Adding an MCP server to ~/.config/opencode/opencode.json is enough: the dashboard shows Konflikt, the toggle turns off, and there is no non-destructive recovery path. The only UI option, restoring the snapshot, would revert the user's newer edits; the only clean fix is manual surgery (delete the provider.opencodex block by hand, then re-enable).
I expected: edits outside the opencodex-owned block (normal, frequent usage of one's own config — MCP servers, instructions, permissions) either don't trip the conflict state, or there is a supported, non-destructive way to re-apply/re-own from the dashboard or CLI.
Root cause — classifyIntegration (src/integrations/state.ts) checks two fingerprints:
if (clientId !== "omp" && fingerprint(input.fileText ?? "") !== input.record.fileFingerprint) {
return { state: "conflict", reason: "foreign-edit" }; // ← fires on ANY file edit
}
if (recordedFragmentFingerprint(input.parsed, input.record) !== input.record.blockFingerprint) {
return { state: "conflict", reason: "foreign-edit" }; // ← the actual tamper check
}
The whole-file check fires even when the second check proves the owned block is untouched. I understand this is deliberate (the test "whole-document serializers still conflict on an unrelated source edit" pins it): non-omp clients rewrite the whole document on apply, so a rewrite could normalize a user's formatting or drop comments, and the classifier fails closed. But the practical effect is that the integration breaks permanently on the first config edit the user makes themselves, with no recovery path.
Proposal — two options, not mutually exclusive; I'm happy to send a PR with regression tests for whichever direction fits the project's invariants:
- A. Confirm-gated re-apply from
conflict when the owned block is intact (mirrors the existing restore --confirm-drift pattern). If recordedFragmentFingerprint(parsed, record) === record.blockFingerprint, apply merges into the current parsed document and a snapshot is taken first, so no user content can be lost — only formatting may normalize. CLI: ocx integration client enable --client opencode --confirm-drift (or similar); dashboard: a confirm dialog instead of the dead-end badge.
- B. Classifier refinement for lossless cases: when the owned block is intact and the file round-trips byte-identically through the client's serializer (
fingerprint(serializeDocument(parsed, format)) === fingerprint(fileText)), classify current/stale instead of conflict — rewriting such a file provably cannot destroy formatting or comments. Files with custom formatting/comments still fail closed and would use path A.
Reproduction
ocx integration client enable --client opencode → state current.
- Add any MCP server (or any other key) to
~/.config/opencode/opencode.json without touching provider.opencodex, e.g.:
"mcp": { "playwright": { "type": "local", "command": ["npx", "-y", "@playwright/mcp@latest"], "enabled": true } }
- Dashboard → Integrations → OpenCode now shows
Konflikt, toggle off.
ocx integration client enable --client opencode refuses with <path> changed after opencodex wrote it.
- No non-destructive way out: "restore this state" reverts the user's own edit from step 2; the only clean recovery is deleting the
provider.opencodex block manually and enabling again.
Version
2.14.2
Operating system
macOS 27 (arm64)
Provider and model
No response
Logs or error output
$ ocx integration client enable --client opencode
refused: /Users/<user>/.config/opencode/opencode.json changed after opencodex wrote it (conflict)
$ ocx integration client status --client opencode
clientId: opencode
state: conflict
Screenshots and supporting files
No response
Redacted configuration
No response
Checks
Client or integration
Other
Area
Other
Summary
The OpenCode (and any non-
omp) client integration dead-ends in a permanentconflictstate as soon as the user makes any edit to their own config file — even edits that never touch the opencodex-owned block. Adding an MCP server to~/.config/opencode/opencode.jsonis enough: the dashboard showsKonflikt, the toggle turns off, and there is no non-destructive recovery path. The only UI option, restoring the snapshot, would revert the user's newer edits; the only clean fix is manual surgery (delete theprovider.opencodexblock by hand, then re-enable).I expected: edits outside the opencodex-owned block (normal, frequent usage of one's own config — MCP servers, instructions, permissions) either don't trip the conflict state, or there is a supported, non-destructive way to re-apply/re-own from the dashboard or CLI.
Root cause —
classifyIntegration(src/integrations/state.ts) checks two fingerprints:The whole-file check fires even when the second check proves the owned block is untouched. I understand this is deliberate (the test "whole-document serializers still conflict on an unrelated source edit" pins it): non-
ompclients rewrite the whole document on apply, so a rewrite could normalize a user's formatting or drop comments, and the classifier fails closed. But the practical effect is that the integration breaks permanently on the first config edit the user makes themselves, with no recovery path.Proposal — two options, not mutually exclusive; I'm happy to send a PR with regression tests for whichever direction fits the project's invariants:
conflictwhen the owned block is intact (mirrors the existingrestore --confirm-driftpattern). IfrecordedFragmentFingerprint(parsed, record) === record.blockFingerprint, apply merges into the current parsed document and a snapshot is taken first, so no user content can be lost — only formatting may normalize. CLI:ocx integration client enable --client opencode --confirm-drift(or similar); dashboard: a confirm dialog instead of the dead-end badge.fingerprint(serializeDocument(parsed, format)) === fingerprint(fileText)), classifycurrent/staleinstead ofconflict— rewriting such a file provably cannot destroy formatting or comments. Files with custom formatting/comments still fail closed and would use path A.Reproduction
ocx integration client enable --client opencode→ statecurrent.~/.config/opencode/opencode.jsonwithout touchingprovider.opencodex, e.g.:Konflikt, toggle off.ocx integration client enable --client opencoderefuses with<path> changed after opencodex wrote it.provider.opencodexblock manually and enabling again.Version
2.14.2
Operating system
macOS 27 (arm64)
Provider and model
No response
Logs or error output
Screenshots and supporting files
No response
Redacted configuration
No response
Checks