feat(kernel): reviewer static-first infra (B§5)
- StaticFinding/StaticFindingSeverity + StaticFindingsRecordedEvent (registered + test) - StaticAnalysisRunner over an injectable CommandRunner; parses compiler/ktlint/detekt finding formats (lenient) into StaticFindings - excludeStaticFindingsFromReview pure filter, live-wired into SessionOrchestrator context assembly so static-tool findings are kept out of the reviewer's context - static_check pipeline stage left as a documented TODO (needs an event-emitting deterministic stage-type seam that doesn't exist yet) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class StaticFindingSeverity { ERROR, WARNING, INFO }
|
||||
|
||||
/**
|
||||
* One deterministic finding from a static-analysis tool (compiler, ktlint, detekt, …).
|
||||
* [tool] is the tool name; [file]/[line]/[column] locate it; [ruleId] is the tool's rule
|
||||
* identifier when it emits one (e.g. detekt's `[MagicNumber]`).
|
||||
*/
|
||||
@Serializable
|
||||
data class StaticFinding(
|
||||
val tool: String,
|
||||
val file: String,
|
||||
val line: Int,
|
||||
val column: Int = 0,
|
||||
val severity: StaticFindingSeverity,
|
||||
val message: String,
|
||||
val ruleId: String? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Deterministic static-analysis output recorded before review so the reviewer can be given a
|
||||
* clean, static-issue-free context (BACKLOG §B-§5): the compile/lint/format findings a tool
|
||||
* already caught are mechanically excluded from the reviewer's context, so the LLM reviewer
|
||||
* spends its attention on semantic review rather than re-finding what the build already reports.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("StaticFindingsRecorded")
|
||||
data class StaticFindingsRecordedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val tool: String,
|
||||
val findings: List<StaticFinding>,
|
||||
) : EventPayload
|
||||
@@ -49,6 +49,7 @@ import com.correx.core.events.events.RetryAttemptedEvent
|
||||
import com.correx.core.events.events.WorkspaceStateObservedEvent
|
||||
import com.correx.core.events.events.RiskAssessedEvent
|
||||
import com.correx.core.events.events.SourceFetchedEvent
|
||||
import com.correx.core.events.events.StaticFindingsRecordedEvent
|
||||
import com.correx.core.events.events.StageCheckpointFailedEvent
|
||||
import com.correx.core.events.events.StageCheckpointPassedEvent
|
||||
import com.correx.core.events.events.StageCompletedEvent
|
||||
@@ -143,6 +144,7 @@ val eventModule = SerializersModule {
|
||||
subclass(SourceFetchedEvent::class)
|
||||
subclass(LowQualityExtractionEvent::class)
|
||||
subclass(EgressHostsGrantedEvent::class)
|
||||
subclass(StaticFindingsRecordedEvent::class)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.correx.core.events.serialization
|
||||
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.StaticFinding
|
||||
import com.correx.core.events.events.StaticFindingSeverity
|
||||
import com.correx.core.events.events.StaticFindingsRecordedEvent
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class StaticAnalysisEventSerializationTest {
|
||||
|
||||
@Test
|
||||
fun `StaticFindingsRecordedEvent round-trips as polymorphic EventPayload`() {
|
||||
val sample: EventPayload = StaticFindingsRecordedEvent(
|
||||
sessionId = SessionId("s"),
|
||||
stageId = StageId("static_check"),
|
||||
tool = "detekt",
|
||||
findings = listOf(
|
||||
StaticFinding(
|
||||
tool = "detekt",
|
||||
file = "src/Main.kt",
|
||||
line = 12,
|
||||
column = 5,
|
||||
severity = StaticFindingSeverity.WARNING,
|
||||
message = "Magic number found",
|
||||
ruleId = "MagicNumber",
|
||||
),
|
||||
StaticFinding(
|
||||
tool = "kotlinc",
|
||||
file = "src/Other.kt",
|
||||
line = 3,
|
||||
severity = StaticFindingSeverity.ERROR,
|
||||
message = "unresolved reference: foo",
|
||||
),
|
||||
),
|
||||
)
|
||||
val encoded = eventJson.encodeToString(EventPayload.serializer(), sample)
|
||||
assertTrue(
|
||||
encoded.contains("\"type\":\"StaticFindingsRecorded\""),
|
||||
"SerialName must be present: $encoded",
|
||||
)
|
||||
val decoded = eventJson.decodeFromString(EventPayload.serializer(), encoded)
|
||||
assertEquals(sample, decoded)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user