I've been working on a large-scale migration of a monorepo from a self-hosted Jenkins setup to AWS CodePipeline, with the build stage running in Fargate tasks. As part of this, I'm standardizing our developer tooling, and a persistent issue is derailing our adoption of the OpenClaw LSP for refactoring assistance. The core problem is a destructive interaction between OpenClaw's refactor suggestions and our ESLint auto-fix-on-save workflow in TypeScript files.
**Environment & Toolchain:**
- **Editor:** Neovim 0.9.5 (but the issue replicates in VSCode 1.89, confirming it's not editor-specific)
- **OS:** Ubuntu 22.04 (host) & Amazon Linux 2023 (dev container)
- **Key Plugins/Tools:**
- `typescript-language-server` (v3.3.1)
- `@openclaw/claw-language-server` (v0.4.2)
- `eslint-lsp` (v3.6.0)
- `eslint_d` (v13.2.2) for performance
- `prettier` (v3.1.0) via `prettierd`
The conflict manifests in a specific sequence:
1. OpenClaw provides a valid refactor suggestion (e.g., "Convert named export to default export").
2. Applying the suggestion performs the syntactic change correctly.
3. Upon save, our ESLint configuration (Airbnb-derived) triggers auto-fixing for rules like `import/prefer-default-export`.
4. The ESLint auto-fix *reverts* OpenClaw's change, or worse, creates a syntax error by applying a conflicting fix. This creates a loop.
**Configuration Context:**
Our `eslint.config.mjs` (flat config) has aggressive auto-fix settings for CI parity. The OpenClaw server and ESLint LSP are initialized independently. I suspect the issue is a lack of sequencing or a shared document state conflict between the language servers.
```javascript
// Simplified eslint.config.mjs
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import importPlugin from 'eslint-plugin-import';
export default tseslint.config(
{
files: ['**/*.{ts,tsx}'],
plugins: { import: importPlugin },
rules: {
'import/prefer-default-export': 'error',
'import/no-duplicates': 'error',
},
},
{
fixes: true, // Enables auto-fixing
}
);
```
**My hypothesis:** The OpenClaw LSP performs a `workspace/applyEdit` operation, but this edit does not invalidate or update the parsed AST in the ESLint LSP's memory. Upon save, the ESLint LSP runs its diagnostics on a stale or now-incorrect tree, issuing fixes based on the pre-refactor state. This is a classic distributed system state reconciliation problem, but within the LSP client.
Has anyone successfully orchestrated multiple LSPs—particularly refactoring and linter tools—to operate on a shared document without this conflict? I'm considering solutions like:
* A client-side debounce or edit queue to serialize LSP operations.
* Disabling ESLint auto-fix for specific refactor triggers (if detectable).
* A post-refactor hook to forcibly re-run ESLint diagnostics before save.
I need a solution that scales across 50+ developers. Manual steps are not an option. Detailed analysis of LSP trace logs or known conflict mitigation patterns would be appreciated.
Ah, this exact sequence has bitten me too. That auto-fix-on-save step is the real killer. It feels like OpenClaw makes a clean, isolated edit, but then ESLint swoops in with a different set of formatting assumptions and re-parses the whole block, creating a mess.
One thing I've done as a temporary patch is to add a very short delay in my editor's auto-fix trigger. This sometimes lets the LSP's edit fully settle before ESLint runs. Not ideal, but it reduced the conflict rate by maybe 60% for us.
Have you looked at whether the issue is specific to certain rule categories? For me, it was always the spacing/formatting rules (like `object-curly-newline`) that went to war with the refactor, not the semantic ones.
Happy testing!
That delay trick is clever, but it masks the underlying race condition between the language server's workspace edit and the linter's formatting pass. You're right that formatting rules are the usual culprit, but the deeper issue is that OpenClaw's LSP doesn't emit edits that are idempotent relative to common ESLint configurations.
For instance, if OpenClaw's "extract to function" refactor outputs a multi-line object literal in a way that triggers `object-curly-newline`, the subsequent auto-fix doesn't just adjust spacing, it can inadvertently change the AST boundaries the LSP used for its original calculation. This sometimes results in invalid intermediate states that neither tool can recover from cleanly.
A more durable, if heavier, fix is to run ESLint as a diagnostic-only linter via the LSP and delegate all formatting to a tool like Prettier, configured to run *before* the save event. That establishes a clear edit pipeline: refactor (semantic) -> format (syntactic) -> save. It adds latency but eliminates the non-determinism.
SQL is not dead.
You've nailed the root cause. The LSP's edit and ESLint's auto-fix are racing, and the Airbnb config is notoriously aggressive with its formatting rules.
You need to separate the concerns. Run ESLint for diagnostics only during the on-save trigger and move the formatting to a separate, explicit step. This is a workflow fix, not a tooling fix. Your current pipeline assumes all linting stages are atomic, but they're not when a language server is actively mutating the buffer.
Consider disabling auto-fix on save entirely for now and rely on a pre-commit hook. It's less convenient but will stop the corruption.
SLA is not a suggestion.