diff --git a/core/events/src/main/kotlin/com/correx/core/events/events/LspDiagnosticEvents.kt b/core/events/src/main/kotlin/com/correx/core/events/events/LspDiagnosticEvents.kt index c9a9de32..c8b64676 100644 --- a/core/events/src/main/kotlin/com/correx/core/events/events/LspDiagnosticEvents.kt +++ b/core/events/src/main/kotlin/com/correx/core/events/events/LspDiagnosticEvents.kt @@ -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 = 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 diff --git a/core/events/src/test/kotlin/com/correx/core/events/events/LspDiagnosticTest.kt b/core/events/src/test/kotlin/com/correx/core/events/events/LspDiagnosticTest.kt new file mode 100644 index 00000000..bb6414c0 --- /dev/null +++ b/core/events/src/test/kotlin/com/correx/core/events/events/LspDiagnosticTest.kt @@ -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) = 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) + } +} diff --git a/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/SessionOrchestratorGates2.kt b/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/SessionOrchestratorGates2.kt index 5c9cd521..bd590d2b 100644 --- a/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/SessionOrchestratorGates2.kt +++ b/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/SessionOrchestratorGates2.kt @@ -97,7 +97,9 @@ internal suspend fun SessionOrchestrator.runLspDiagnostics( sessionId, LspDiagnosticsCompletedEvent(sessionId, stageId, result.server, diagnostics, result.skippedReason), ) - val errors = diagnostics.filter { it.severity.equals("error", ignoreCase = true) } + // Lint-class diagnostics (unused import, deprecated) are recorded above but never gate: a + // tsconfig with noUnusedLocals promotes them to "error" severity, which no rewrite can clear. + val errors = diagnostics.filter { it.severity.equals("error", ignoreCase = true) && !it.isLint } if (errors.isEmpty()) return StageExecutionResult.Success(emptyList()) val detail = errors.joinToString("\n") { "- ${it.path}:${it.line + 1}:${it.character + 1} ${it.code.orEmpty()} ${it.message}".trim() diff --git a/infrastructure/workflow/src/main/kotlin/com/correx/infrastructure/workflow/Lsp4jDiagnosticsRunner.kt b/infrastructure/workflow/src/main/kotlin/com/correx/infrastructure/workflow/Lsp4jDiagnosticsRunner.kt index d3c7d29e..605f4e21 100644 --- a/infrastructure/workflow/src/main/kotlin/com/correx/infrastructure/workflow/Lsp4jDiagnosticsRunner.kt +++ b/infrastructure/workflow/src/main/kotlin/com/correx/infrastructure/workflow/Lsp4jDiagnosticsRunner.kt @@ -132,6 +132,7 @@ class Lsp4jDiagnosticsRunner( severity = diagnostic.severity?.name?.lowercase() ?: "error", code = diagnostic.code?.let { if (it.isLeft) it.left else it.right.toString() }, message = diagnostic.message, + tags = diagnostic.tags.orEmpty().map { it.name.lowercase() }, ) } }