fix(lsp-gate): lint-tagged diagnostics never fail a stage (#311)

tsserver tags TS6133 (unused import) Unnecessary, but a tsconfig with
noUnusedLocals promotes it to error severity — which hard-failed whole
runs no rewrite could clear. Carry LSP DiagnosticTag through the
LspDiagnostic event and gate on untagged errors only; lint diagnostics
stay recorded and visible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 21:16:33 +04:00
parent c32445b8a8
commit f61864ff1d
4 changed files with 38 additions and 2 deletions
@@ -13,7 +13,12 @@ data class LspDiagnostic(
val severity: String,
val code: String? = null,
val message: String,
)
/** LSP `DiagnosticTag` names, lowercased ("unnecessary", "deprecated"). Lint class, not severity. */
val tags: List<String> = emptyList(),
) {
/** Lint-class diagnostic: reported and recorded, but never a reason to fail a stage. */
val isLint: Boolean get() = tags.isNotEmpty()
}
/** Recorded LSP 3.17 pull-diagnostic observation; replay never re-queries a language server. */
@Serializable
@@ -0,0 +1,28 @@
package com.correx.core.events.events
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class LspDiagnosticTest {
private fun diagnostic(tags: List<String>) = LspDiagnostic(
path = "src/App.tsx",
line = 0,
character = 0,
severity = "error",
code = "6133",
message = "'React' is declared but its value is never read.",
tags = tags,
)
@Test
fun `tagged diagnostic is lint class even at error severity`() {
assertTrue(diagnostic(listOf("unnecessary")).isLint)
assertTrue(diagnostic(listOf("deprecated")).isLint)
}
@Test
fun `untagged diagnostic still gates`() {
assertFalse(diagnostic(emptyList()).isLint)
}
}