feat(kernel): static-first reviewer gate (role-reliability §5)

Run operator-configured static-analysis commands (compiler/detekt/formatters) as a
deterministic harness step on the producing stage, before the LLM reviewer. A stage
declares `static_analysis = [commands]`; after it produces its artifact, each command
runs in the workspace root and the results are recorded in a StaticAnalysisCompletedEvent
(invariant #9 env observation — recorded live, folded on replay, never re-run). A
non-clean command fails the stage retryably with its output fed back verbatim (§2:
compiler/test output is ground truth), so the implementer fixes it and only static-clean
code ever reaches the reviewer. This makes §5's "reviewer context excludes static findings"
mechanical — the findings are resolved upstream, so the reviewer can't waste inference on
what detekt catches for free; no output-parsing, no context plumbing.

- StaticAnalysisCompletedEvent + StaticAnalysisFinding (core:events) + registration.
- StaticAnalysisRunner seam + ProcessStaticAnalysisRunner (whitespace-split argv, stderr
  merged, timeout→nonzero exit) in core:kernel; wired (nullable) into OrchestratorEngines
  and the server. Null runner / no workspace root → logged no-op, never blocks the run.
- StageConfig.staticAnalysis + `static_analysis` TOML field; runStaticAnalysis folded into
  a runPostStageGates sequence (produces → grounding → echo → static analysis).
- Documented `static_analysis` on the role_pipeline implementer stage (commented; commands
  are workspace-specific). The reviewer prompt half shipped in 3467826.
This commit is contained in:
2026-06-14 23:51:49 +04:00
parent 0d8df4a130
commit 365eb8a279
13 changed files with 354 additions and 9 deletions
@@ -34,6 +34,7 @@ import com.correx.core.kernel.orchestration.OrchestrationConfig
import com.correx.core.kernel.orchestration.OrchestrationProjector
import com.correx.core.kernel.orchestration.OrchestrationRepository
import com.correx.core.kernel.orchestration.OrchestratorEngines
import com.correx.core.kernel.orchestration.ProcessStaticAnalysisRunner
import com.correx.core.kernel.orchestration.OrchestratorRepositories
import com.correx.core.kernel.orchestration.WorkspaceContext
import com.correx.core.kernel.orchestration.WorkspaceTools
@@ -277,6 +278,7 @@ fun main() {
toolCallAssessor = toolCallAssessor,
workspacePolicy = workspacePolicy,
workspaceToolRegistryProvider = wsToolRegistryProvider,
staticAnalysisRunner = ProcessStaticAnalysisRunner(),
)
val decisionJournalRepository = InfrastructureModule.createDecisionJournalRepository(eventStore)
val defaultOrchestrationConfig = OrchestrationConfig(