= core-validation == purpose Runs a pipeline of validators against a `ValidationContext` (containing the workflow graph, detected cycles, cycle policies, and session state) to produce a `ValidationReport`. If structural errors are found, the pipeline rejects immediately as non-retryable. If issues warrant human review, it signals `NeedsApproval`. == responsibilities * Validating workflow graph structure (start node exists, no dangling transitions) * Validating transition integrity (endpoint references, deterministic ordering) * Validating session temporal and status consistency * Running semantic rules (e.g. cycle policy binding checks) * Validating artifact payloads against their declared schemas * Producing a `ValidationOutcome` (Passed, Rejected, or NeedsApproval) * Determining whether validation failures are retryable * Triggering approval requests when validation risk exceeds thresholds == non-responsibilities * Does not enforce approvals or block execution — only signals the need for approval * Does not modify state or emit events — it is a pure validation function * Does not manage the lifecycle of validation reports * Does not validate LLM outputs directly — validates artifacts and graph structures == key types === Validator * **kind**: fun interface * **purpose**: Performs a single validation pass and returns a `ValidationSection`. === ValidationPipeline * **kind**: class * **purpose**: Runs a list of validators in sequence. Short-circuits on structural errors (non-retryable rejection). Optionally evaluates an `ApprovalTrigger` after all validators pass. * **fields**: `validators` (list), `approvalTrigger` (nullable) === ValidationOutcome * **kind**: sealed interface * **purpose**: Result of a validation run. * **variants**: * `Passed(report)` — validation succeeded * `Rejected(report, retryable)` — validation failed; `retryable` is set by the validator and must not be overridden * `NeedsApproval(request)` — validation passed but human approval is required === ValidationReport * **kind**: data class * **purpose**: Collection of `ValidationSection`s produced by a pipeline run. === ValidationSection * **kind**: data class * **purpose**: Named group of issues from a single validator. * **fields**: `name`, `issues`, `metadata` === ValidationIssue * **kind**: data class * **purpose**: A single finding with machine-readable code, human message, severity, and optional location. === ValidationSeverity * **kind**: enum * **variants**: `INFO`, `WARNING`, `ERROR` === ValidationLocation * **kind**: sealed interface * **purpose**: Points to where an issue was found. * **variants**: * `Graph(stageId, transitionId)` — issue in workflow graph * `Session(sessionId)` — issue in session state === ValidationContext * **kind**: data class * **purpose**: Input to validators, containing the workflow graph, cycles, policies, and session state. * **fields**: `graph` (WorkflowGraph), `detectedCycles`, `cyclePolicies`, `sessionState` === GraphValidator * **kind**: class * **purpose**: Checks start node existence, dangling transitions, and records detected cycles as INFO issues. === TransitionValidator * **kind**: class * **purpose**: Checks transition endpoint integrity and deterministic ordering per source node. === SessionValidator * **kind**: class * **purpose**: Checks temporal consistency, negative invalid transition count, and suspicious failure states. === SemanticValidator * **kind**: class * **purpose**: Runs a list of `SemanticRule` instances. === SemanticRule * **kind**: interface * **purpose**: Validates a single semantic property. === CyclePolicyBindingRule * **kind**: class * **purpose**: Warns when detected cycles lack a policy binding. Only active when `requirePolicyForCycles` is true. === ArtifactPayloadValidator * **kind**: class * **purpose**: Validates artifact payloads (`FileWrittenArtifact`, `ProcessResultArtifact`) against their schemas by reading from `ArtifactStore`. === ApprovalTrigger * **kind**: class * **purpose**: Evaluates a `ValidationReport` and returns an `ApprovalRequest` if error count > 0 or cycle policies are missing. === ApprovalRequest (validation) * **kind**: data class * **purpose**: Signals that an operation requires human approval. * **fields**: `sessionId` (SessionId?, nullable — may be absent at early validation stage), `riskSummary` (ValidationRiskStats), `validationReport` === ValidationRiskStats * **kind**: data class * **purpose**: Summary of error and warning counts plus cycle policy status. == event flow *inbound:* * None. Validation is invoked by the orchestrator before executing operations. It does not subscribe to events. *outbound:* * None. Validation is a pure function — it returns a sealed outcome, it does not emit events. == integration points * `:core:events` — shared identity types (SessionId, StageId, etc.) * `:core:sessions` — `SessionState`, `SessionStatus` * `:core:transitions` — `WorkflowGraph`, `DetectedCycle`, `CyclePolicyBinding`, `TransitionEdge`, `TransitionOrdering`, `CycleSignatureFactory` * `:core:artifacts` — `FileWrittenArtifact`, `ProcessResultArtifact`, `TypedArtifactSlot` * `:core:artifacts-store` — `ArtifactStore` == invariants * `ValidationOutcome.Rejected.retryable` is set by the validator and must not be overridden by the orchestrator * Structural validation errors (ERROR severity in the graph) produce `retryable = false` * Pipeline short-circuits on the first ERROR severity issue — remaining validators are skipped * `ApprovalRequest` produced by `ApprovalTrigger` is implicitly `Tier.T2 (REVERSIBLE)` — validation affects workflow progression, not external state * The `RiskSummary` and `ValidationRiskStats` types are duplicate definitions of the same data == PlantUML diagram [plantuml, core-validation, "png"] ---- include::../../diagrams/core-validation.puml[] ---- == known issues * `RiskSummary` and `ValidationRiskStats` are separate data classes with identical fields — likely a leftover from refactoring. * `ApprovalRequest` lives in the validation module but is consumed by the approvals module, which has its own `DomainApprovalRequest` type. These are two different representations of the same concept with no shared type. == open questions * Should the `ApprovalRequest` produced by validation share a common type with the approvals module instead of being a validation-local class? * Should validation produce structured IDs for reports and risk summaries that are meaningful to the approvals module?