= core-kernel

== purpose

The orchestration engine that binds all core modules together into a running workflow. It owns the execution loop: resolve the next transition, execute the stage (build context, run inference, dispatch tool calls, validate), handle approvals, manage retries, and persist results as events. This is where the deterministic core meets nondeterministic inputs — the kernel is the decision-maker that translates LLM proposals and tool results into recorded state changes.

== responsibilities

* Defines `SessionOrchestrator` (abstract class) — the core execution loop shared by live and replay modes
* Implements `DefaultSessionOrchestrator` (concrete) — the live execution orchestrator with real inference, validation, risk assessment, and approval gating
* Implements `ReplayOrchestrator` — environment-independent replay using recorded events, with configurable `ReplayStrategy` (Full, SkipInference, SkipValidation)
* Provides `ApprovalGateway` interface — entry point for external approval decisions (CLI, server)
* Defines `OrchestrationReducer` / `DefaultOrchestrationReducer` — reduces workflow lifecycle events to `OrchestrationState`
* Provides `OrchestrationProjector` and `OrchestrationRepository` — rebuild orchestration state from events
* Defines `OrchestratorRepositories` and `OrchestratorEngines` — dependency injection data classes wiring all required services
* Implements `RetryCoordinator` / `DefaultRetryCoordinator` — records `RetryAttemptedEvent` and applies backoff delay
* Provides `ReplayInferenceProvider` — fake inference provider for replay that reads recorded `InferenceCompletedEvent` artifacts
* Defines `WorkflowResult` sealed interface (Completed, Failed, Cancelled) and `ReplayStrategy` (Full, SkipInference, SkipValidation)
* Defines `OrchestrationConfig` — retry policy, replay strategy, stage timeout, sandbox root, default system prompt path
* Provides `ReplayArtifactMissingException` — thrown when replay cannot find a recorded inference artifact

== non-responsibilities

* Does not define workflow graphs, stage configs, or transition conditions — those are in `:core:transitions`
* Does not define event types — those are in `:core:events`
* Does not implement the event store, inference providers, or tool executors — those are in `:infrastructure`
* Does not define session lifecycle state — that is in `:core:sessions`
* Does not define approval domain models — those are in `:core:approvals`
* Does not know about specific LLM APIs, shell commands, or filesystem layout

== key types

=== SessionOrchestrator
* **kind**: abstract class
* **purpose**: base orchestrator with shared stage execution logic (context building, inference, tool dispatch, approval handling, validation)
* **key methods**: `run(sessionId, graph, config)`, `cancel(sessionId)`, `executeStage(...)`, `runInference(...)`, `resolveTransition(...)`, `emit(...)`
* **fields**: repositories (OrchestratorRepositories), engines (OrchestratorEngines), artifactStore, cancellations map, pendingApprovals map

=== DefaultSessionOrchestrator
* **kind**: class
* **purpose**: live orchestrator. Uses `tailrec` step loop: resolve transition → execute stage → repeat. Integrates real inference, validation pipeline, risk assessment, and approval gating.

=== ReplayOrchestrator
* **kind**: class
* **purpose**: deterministic replay orchestrator. Overrides `runInference` and `mapValidationOutcome` to use recorded data instead of live services. Uses `NoOpApprovalEngine` and `NoOpRiskAssessor`.

=== OrchestratorRepositories
* **kind**: data class
* **purpose**: DI holder for all repository dependencies: eventStore, inferenceRepository, orchestrationRepository, sessionRepository, artifactRepository, approvalRepository

=== OrchestratorEngines
* **kind**: data class
* **purpose**: DI holder for all engine/service dependencies: transitionResolver, contextPackBuilder, inferenceRouter, validationPipeline, approvalEngine, riskAssessor, promptResolver, toolExecutor, toolRegistry

=== ApprovalGateway
* **kind**: interface
* **purpose**: single method `submitApprovalDecision(requestId, decision)` — bridge between external approval UI and the orchestrator's pending approval futures

=== OrchestrationReducer / DefaultOrchestrationReducer
* **kind**: interface / class
* **purpose**: reduces `WorkflowStartedEvent`, `WorkflowCompletedEvent`, `WorkflowFailedEvent`, `OrchestrationPausedEvent`, `OrchestrationResumedEvent`, `RetryAttemptedEvent` to `OrchestrationState`

=== OrchestrationProjector
* **kind**: class
* **purpose**: wraps `OrchestrationReducer` as a `Projection<OrchestrationState>`

=== OrchestrationRepository
* **kind**: class
* **purpose**: wraps `EventReplayer<OrchestrationState>` — `getState(sessionId)` returns the rebuilt orchestration state

=== OrchestrationConfig
* **kind**: data class
* **purpose**: runtime configuration for an orchestration run
* **fields**: retryPolicy, replayStrategy, stageTimeoutMs, sandboxRoot, defaultSystemPromptPath

=== WorkflowResult
* **kind**: sealed interface
* **purpose**: top-level result of a workflow execution
* **variants**: Completed(sessionId, terminalStageId), Failed(sessionId, reason, retryExhausted), Cancelled(sessionId)

=== ReplayStrategy
* **kind**: sealed interface
* **purpose**: controls replay behavior
* **variants**: Full (re-execute everything), SkipInference (use recorded artifacts), SkipValidation (trust recorded outcomes)

=== StageOutcome
* **kind**: sealed interface
* **purpose**: stage execution outcome model (success, validation failure, inference failure, approval required, cancelled)
* **variants**: Success(artifact), ValidationFailure(report, retryable), InferenceFailure(reason, retryable), ApprovalRequired(request), Cancelled

=== RetryCoordinator
* **kind**: interface
* **purpose**: decides whether to retry a failed stage
* **method**: `shouldRetry(sessionId, stageId, currentAttempt, policy, failureReason): Boolean`

=== DefaultRetryCoordinator
* **kind**: class
* **purpose**: emits `RetryAttemptedEvent`, applies backoff delay via `kotlinx.coroutines.delay`

=== ReplayInferenceProvider
* **kind**: class
* **purpose**: fake `InferenceProvider` for replay — finds the first `InferenceCompletedEvent` for the given stage and returns an empty-text response with recorded token/latency data

=== ReplayArtifactMissingException
* **kind**: class
* **purpose**: thrown when replay cannot find a recorded inference artifact for a given stage

=== InferenceResult (internal)
* **kind**: sealed interface (internal to SessionOrchestrator)
* **purpose**: internal inference outcome type
* **variants**: Success(response), Failed(reason), Cancelled

== event flow

*inbound (via ApprovalGateway):*
* `ApprovalDecision` submitted externally → kernel resolves pending approval `CompletableDeferred`

*outbound (via SessionOrchestrator.emit):*
The kernel emits events for every significant action:

Session lifecycle:
* `WorkflowStartedEvent` — workflow begins
* `WorkflowCompletedEvent` — workflow ends normally
* `WorkflowFailedEvent` — workflow ends with failure or cancellation

Orchestration lifecycle:
* `OrchestrationPausedEvent` — paused for approval or user request
* `OrchestrationResumedEvent` — unblocked

Stage execution:
* `InferenceStartedEvent` — LLM inference began
* `InferenceCompletedEvent` — LLM inference completed
* `InferenceFailedEvent` — LLM inference failed
* `InferenceTimeoutEvent` — LLM inference timed out

Tool execution:
* `ToolInvocationRequestedEvent` — tool call was requested (may require approval)
* `ArtifactCreatedEvent` / `ArtifactValidatingEvent` / `ArtifactValidatedEvent` — tool-produced artifacts

Approvals:
* `ApprovalRequestedEvent` — approval requested for tool or validation
* `RiskAssessedEvent` — risk assessment result
* `ApprovalDecisionResolvedEvent` — decision recorded
* `OrchestrationResumedEvent` (after approval pass) — resumed

== integration points

* `:core:events` — `EventStore`, `EventMetadata`, `NewEvent`, `EventId`, all event types, identity types, `Projection`, `EventReplayer`
* `:core:sessions` — `DefaultSessionRepository`, `Session`
* `:core:transitions` — `TransitionResolver`, `WorkflowGraph`, `StageConfig`, `StageExecutionResult`, `EvaluationContext`, `PromptResolver`
* `:core:validation` — `ValidationPipeline`, `ValidationContext`, `ValidationOutcome`
* `:core:approvals` — `DomainApprovalRequest`, `ApprovalDecision`, `NoOpApprovalEngine`, `DefaultApprovalRepository`
* `:core:context` — `ContextPackBuilder`, `ContextPack`, `ContextEntry`, `ContextLayer`, `TokenBudget`
* `:core:inference` — `InferenceRouter`, `InferenceRequest`, `InferenceResponse`, `InferenceRepository`, `GenerationConfig`, `ResponseFormat`, `ToolCallRequest`, `ToolDefinition`, `ToolFunction`, `FinishReason`, `ProviderHealth`
* `:core:tools` — `ToolExecutor`, `ToolResult`, `ToolRegistry`
* `:core:artifacts` — `ArtifactState`, `TypedArtifactSlot`, `ArtifactRepository`
* `:core:artifacts-store` — `ArtifactStore`
* `:core:risk` — `RiskAssessor`, `RiskContext`

== invariants

* `DefaultSessionOrchestrator.run()` is the only path for live workflow execution — replay uses `ReplayOrchestrator.run()` instead.
* Cancellation is cooperative: the orchestrator checks `isCancelled()` at the start of each step and after inference.
* Approval decisions are synchronous: the orchestrator blocks on `CompletableDeferred.await()` until `submitApprovalDecision()` is called.
* Retry is event-driven: each retry attempt emits a `RetryAttemptedEvent` that feeds back into `OrchestrationState.retryCount`.
* The step loop is `tailrec` — no stack overflow from deep workflows.
* Replay never calls live services: `ReplayOrchestrator` replaces `approvalEngine` with `NoOpApprovalEngine`, `riskAssessor` with `NoOpRiskAssessor`, and overrides `runInference` and `mapValidationOutcome`.

== PlantUML diagram

[plantuml, core-kernel, "png"]
----
include::../../diagrams/core-kernel.puml[]
----




== known issues

* `OrchestrationState.kt` and `OrchestrationStatus.kt` in this module are empty — the actual types live in `:core:events` under `com.correx.core.events.orchestration`. The kernel's `DefaultOrchestrationReducer` imports from the events module's copies.
* `StageOutcome` appears to be defined but unused within the kernel — the orchestrator uses `StageExecutionResult` (from `:core:transitions`) instead.
* `RetryPolicy.kt` in `core/kernel/execution/` is an empty file — the actual `RetryPolicy` type is in `:core:events`.
* `SessionOrchestrator` is marked abstract but has no abstract methods — `run()` and `cancel()` are abstract but the class could reasonably be concrete. The abstraction exists solely to share logic between `DefaultSessionOrchestrator` and `ReplayOrchestrator`.

== open questions

* The relationship between `StageOutcome` (in kernel) and `StageExecutionResult` (in transitions) is unclear — they model the same concept with different shapes. One may be dead code.
* The `sandboxRoot` field in `OrchestrationConfig` is declared but not used anywhere in the kernel source.
