refactor: merge cycle policy into semantic validation layer
Wire SemanticValidator into the production validator pipeline and consolidate cycle-policy types under core:validation. - Add SemanticValidator(CycleExitRule(requirePolicyForCycles=false)) to the prod ValidationPipeline (no-op default, preserves behavior) - Move CyclePolicy/Binding/Signature/Factory from core:transitions.policy to core:validation.policy (validation already owns ValidationContext) - Rename CyclePolicyBindingRule -> CycleExitRule (issue code unchanged) - Delete dead CyclePolicyResolver + PolicyValidation
This commit is contained in:
@@ -35,6 +35,8 @@ import com.correx.core.tools.registry.ToolRegistry
|
|||||||
import com.correx.core.transitions.resolution.DefaultTransitionResolver
|
import com.correx.core.transitions.resolution.DefaultTransitionResolver
|
||||||
import com.correx.core.validation.artifact.ArtifactPayloadValidator
|
import com.correx.core.validation.artifact.ArtifactPayloadValidator
|
||||||
import com.correx.core.validation.pipeline.ValidationPipeline
|
import com.correx.core.validation.pipeline.ValidationPipeline
|
||||||
|
import com.correx.core.validation.semantic.SemanticValidator
|
||||||
|
import com.correx.core.validation.semantic.rules.CycleExitRule
|
||||||
import com.correx.infrastructure.InfrastructureModule
|
import com.correx.infrastructure.InfrastructureModule
|
||||||
import com.correx.infrastructure.artifactscas.DefaultMaterializingArtifactWriter
|
import com.correx.infrastructure.artifactscas.DefaultMaterializingArtifactWriter
|
||||||
import com.correx.infrastructure.inference.DefaultProviderRegistry
|
import com.correx.infrastructure.inference.DefaultProviderRegistry
|
||||||
@@ -106,7 +108,16 @@ fun main() {
|
|||||||
transitionResolver = DefaultTransitionResolver { condition, ctx -> condition.evaluate(ctx) },
|
transitionResolver = DefaultTransitionResolver { condition, ctx -> condition.evaluate(ctx) },
|
||||||
contextPackBuilder = DefaultContextPackBuilder(DefaultContextCompressor()),
|
contextPackBuilder = DefaultContextPackBuilder(DefaultContextCompressor()),
|
||||||
inferenceRouter = inferenceRouter,
|
inferenceRouter = inferenceRouter,
|
||||||
validationPipeline = ValidationPipeline(validators = listOf(ArtifactPayloadValidator(artifactStore))),
|
validationPipeline = ValidationPipeline(
|
||||||
|
validators = listOf(
|
||||||
|
ArtifactPayloadValidator(artifactStore),
|
||||||
|
SemanticValidator(
|
||||||
|
rules = listOf(
|
||||||
|
CycleExitRule(requirePolicyForCycles = false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
approvalEngine = approvalEngine,
|
approvalEngine = approvalEngine,
|
||||||
riskAssessor = DefaultRiskAssessor(),
|
riskAssessor = DefaultRiskAssessor(),
|
||||||
promptResolver = { path -> InfrastructureModule.createPromptLoader().load(path) },
|
promptResolver = { path -> InfrastructureModule.createPromptLoader().load(path) },
|
||||||
|
|||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
package com.correx.core.transitions.policy
|
|
||||||
|
|
||||||
class CyclePolicyResolver(
|
|
||||||
private val bindings: Set<CyclePolicyBinding>
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun resolve(signature: CycleSignature): CyclePolicy? {
|
|
||||||
return bindings
|
|
||||||
.firstOrNull { it.cycle == signature }
|
|
||||||
?.policy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
package com.correx.core.transitions.policy
|
|
||||||
|
|
||||||
class PolicyValidation {
|
|
||||||
fun validate(
|
|
||||||
bindings: Set<CyclePolicyBinding>,
|
|
||||||
knownCycles: Set<CycleSignature>
|
|
||||||
): List<String> {
|
|
||||||
|
|
||||||
val errors = mutableListOf<String>()
|
|
||||||
|
|
||||||
val bound = bindings.map { it.cycle }.toSet()
|
|
||||||
|
|
||||||
val unbound = knownCycles - bound
|
|
||||||
|
|
||||||
if (unbound.isNotEmpty()) {
|
|
||||||
errors += unbound.map {
|
|
||||||
"Cycle $it has no policy binding"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -3,7 +3,7 @@ package com.correx.core.validation.model
|
|||||||
import com.correx.core.sessions.SessionState
|
import com.correx.core.sessions.SessionState
|
||||||
import com.correx.core.transitions.analysis.DetectedCycle
|
import com.correx.core.transitions.analysis.DetectedCycle
|
||||||
import com.correx.core.transitions.graph.WorkflowGraph
|
import com.correx.core.transitions.graph.WorkflowGraph
|
||||||
import com.correx.core.transitions.policy.CyclePolicyBinding
|
import com.correx.core.validation.policy.CyclePolicyBinding
|
||||||
|
|
||||||
data class ValidationContext(
|
data class ValidationContext(
|
||||||
val graph: WorkflowGraph,
|
val graph: WorkflowGraph,
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.correx.core.transitions.policy
|
package com.correx.core.validation.policy
|
||||||
|
|
||||||
sealed interface CyclePolicy {
|
sealed interface CyclePolicy {
|
||||||
data class Retry(
|
data class Retry(
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.correx.core.transitions.policy
|
package com.correx.core.validation.policy
|
||||||
|
|
||||||
data class CyclePolicyBinding(
|
data class CyclePolicyBinding(
|
||||||
val cycle: CycleSignature,
|
val cycle: CycleSignature,
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.correx.core.transitions.policy
|
package com.correx.core.validation.policy
|
||||||
|
|
||||||
import com.correx.core.events.types.StageId
|
import com.correx.core.events.types.StageId
|
||||||
import java.util.*
|
import java.util.*
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.correx.core.transitions.policy
|
package com.correx.core.validation.policy
|
||||||
|
|
||||||
import com.correx.core.events.types.StageId
|
import com.correx.core.events.types.StageId
|
||||||
import com.correx.core.transitions.graph.TransitionEdge
|
import com.correx.core.transitions.graph.TransitionEdge
|
||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
package com.correx.core.validation.semantic.rules
|
package com.correx.core.validation.semantic.rules
|
||||||
|
|
||||||
import com.correx.core.transitions.policy.CycleSignatureFactory
|
import com.correx.core.validation.policy.CycleSignatureFactory
|
||||||
import com.correx.core.validation.model.ValidationContext
|
import com.correx.core.validation.model.ValidationContext
|
||||||
import com.correx.core.validation.model.ValidationIssue
|
import com.correx.core.validation.model.ValidationIssue
|
||||||
import com.correx.core.validation.model.ValidationSeverity
|
import com.correx.core.validation.model.ValidationSeverity
|
||||||
import com.correx.core.validation.semantic.SemanticRule
|
import com.correx.core.validation.semantic.SemanticRule
|
||||||
|
|
||||||
class CyclePolicyBindingRule(
|
class CycleExitRule(
|
||||||
private val requirePolicyForCycles: Boolean
|
private val requirePolicyForCycles: Boolean
|
||||||
) : SemanticRule {
|
) : SemanticRule {
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ import com.correx.core.validation.model.ValidationContext
|
|||||||
import com.correx.core.validation.pipeline.ValidationOutcome
|
import com.correx.core.validation.pipeline.ValidationOutcome
|
||||||
import com.correx.core.validation.pipeline.ValidationPipeline
|
import com.correx.core.validation.pipeline.ValidationPipeline
|
||||||
import com.correx.core.validation.semantic.SemanticValidator
|
import com.correx.core.validation.semantic.SemanticValidator
|
||||||
import com.correx.core.validation.semantic.rules.CyclePolicyBindingRule
|
import com.correx.core.validation.semantic.rules.CycleExitRule
|
||||||
import com.correx.core.validation.transition.TransitionValidator
|
import com.correx.core.validation.transition.TransitionValidator
|
||||||
import com.correx.testing.fixtures.CycleFixtures
|
import com.correx.testing.fixtures.CycleFixtures
|
||||||
import com.correx.testing.fixtures.WorkflowFixtures
|
import com.correx.testing.fixtures.WorkflowFixtures
|
||||||
@@ -27,7 +27,7 @@ class ValidationPipelineIntegrationTest {
|
|||||||
TransitionValidator(TransitionOrdering.comparator),
|
TransitionValidator(TransitionOrdering.comparator),
|
||||||
SemanticValidator(
|
SemanticValidator(
|
||||||
rules = listOf(
|
rules = listOf(
|
||||||
CyclePolicyBindingRule(requirePolicyForCycles = true),
|
CycleExitRule(requirePolicyForCycles = true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -70,4 +70,35 @@ class ValidationPipelineIntegrationTest {
|
|||||||
assertInstanceOf(ValidationOutcome.Rejected::class.java, outcome)
|
assertInstanceOf(ValidationOutcome.Rejected::class.java, outcome)
|
||||||
assertFalse((outcome as ValidationOutcome.Rejected).retryable)
|
assertFalse((outcome as ValidationOutcome.Rejected).retryable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `SemanticValidator with requirePolicyForCycles = false ignores unbound cycles`(): Unit = runBlocking {
|
||||||
|
// Verify that when requirePolicyForCycles is false, SemanticValidator returns no issues
|
||||||
|
// even if there are unbound cycles. This is the production default behavior.
|
||||||
|
val graph = WorkflowFixtures.simpleGraph()
|
||||||
|
val cycles = listOf(CycleFixtures.simpleCycle())
|
||||||
|
|
||||||
|
val pipelineWithFalseFlagValidator = ValidationPipeline(
|
||||||
|
validators = listOf(
|
||||||
|
SemanticValidator(
|
||||||
|
rules = listOf(
|
||||||
|
CycleExitRule(requirePolicyForCycles = false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
approvalTrigger = null
|
||||||
|
)
|
||||||
|
|
||||||
|
val context = ValidationContext(
|
||||||
|
graph = graph,
|
||||||
|
detectedCycles = cycles,
|
||||||
|
cyclePolicies = emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val outcome = pipelineWithFalseFlagValidator.validate(context)
|
||||||
|
|
||||||
|
// With requirePolicyForCycles = false, the rule returns no issues,
|
||||||
|
// so the outcome should be Passed (not NeedsApproval).
|
||||||
|
assertInstanceOf(ValidationOutcome.Passed::class.java, outcome)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user