= core-sessions

== purpose

Defines the session lifecycle model: session creation, status transitions (created → active → paused/completed/failed), and the projection infrastructure for rebuilding session state from events. Sessions are the top-level unit of work in Correx — a single workflow execution that produces artifacts and consumes LLM inference.

== responsibilities

* Defines `SessionState` (status, timestamps, invalid transition count) and `SessionStatus` enum (CREATED, ACTIVE, PAUSED, COMPLETED, FAILED)
* Implements `SessionReducer` (interface) and `DefaultSessionReducer` (concrete) — translates session and stage lifecycle events into `SessionState` changes
* Provides `SessionProjector` — adapts `SessionReducer` into the `Projection<SessionState>` interface for use with the replay infrastructure
* Provides `DefaultSessionRepository` — rebuilds `Session` (sessionId + state) from events via `EventReplayer`
* Provides `SessionCounterProjection` / `SessionCounterState` — a simple projection that counts events per session
* Defines `TransitionResult` sealed interface — models whether a status transition was applied or rejected
* Defines `ApprovalMode` enum (DENY, PROMPT, AUTO, YOLO) — the approval policy mode for a session

== non-responsibilities

* Does not produce events — all events are emitted by the kernel (`core:kernel`)
* Does not manage workflow graphs, transitions, or stage execution
* Does not interact with inference, tools, or context
* Does not enforce session lifecycle invariants — it only computes derived state from events
* Does not handle session persistence or storage

== key types

=== SessionStatus
* **kind**: enum
* **purpose**: lifecycle status of a session
* **variants**: CREATED, ACTIVE, PAUSED, COMPLETED, FAILED

=== SessionState
* **kind**: data class
* **purpose**: derived projection state for a session
* **fields**: status (SessionStatus), createdAt (Instant?), updatedAt (Instant?), invalidTransitions (Int)

=== Session
* **kind**: data class
* **purpose**: aggregate holding a sessionId and its current derived state

=== SessionReducer
* **kind**: interface
* **purpose**: reduces a `StoredEvent` into a `SessionState` transition

=== DefaultSessionReducer
* **kind**: class
* **purpose**: concrete reducer — maps `SessionStartedEvent` → ACTIVE, `SessionPausedEvent` → PAUSED, `SessionCompletedEvent` → COMPLETED, `SessionFailedEvent`/`StageFailedEvent` → FAILED, stage progress events → ACTIVE

=== SessionProjector
* **kind**: class
* **purpose**: wraps `SessionReducer` as a `Projection<SessionState>` with initial state = CREATED

=== DefaultSessionRepository
* **kind**: class
* **purpose**: wraps `EventReplayer<SessionState>` to provide `getSession(sessionId)` and `rebuild(sessionId)` returning a `Session`

=== TransitionResult
* **kind**: sealed interface
* **purpose**: result of attempting a session status transition
* **variants**: Applied(newState), Rejected

=== ApprovalMode
* **kind**: enum
* **purpose**: approval policy mode for a session
* **variants**: DENY (block all), PROMPT (ask user), AUTO (auto-approve), YOLO (skip approval entirely)

=== SessionCounterProjection / SessionCounterState
* **kind**: class / data class
* **purpose**: trivial projection counting events per session. Likely a diagnostic or testing utility.

== event flow

*inbound (consumed by reducer):*
* `SessionStartedEvent` → status becomes ACTIVE, createdAt recorded
* `SessionPausedEvent` → status becomes PAUSED
* `SessionResumedEvent` → status becomes ACTIVE
* `SessionCompletedEvent` → status becomes COMPLETED
* `SessionFailedEvent` → status becomes FAILED
* `StageStartedEvent` → status becomes ACTIVE
* `StageCompletedEvent` → status becomes ACTIVE
* `StageFailedEvent` → status becomes FAILED
* `TransitionExecutedEvent` → status becomes ACTIVE

*outbound:*
This module does not emit events. All events are emitted by `core:kernel`.

== integration points

* `:core:events` — `StoredEvent`, `Projection`, `EventReplayer`, `DefaultStateBuilder`, session event types (`SessionStartedEvent`, etc.), stage event types (`StageStartedEvent`, `StageCompletedEvent`, `StageFailedEvent`, `TransitionExecutedEvent`), identity types
* `:core:kernel` — uses `DefaultSessionRepository`, `Session`

== invariants

* `SessionState` is always derived from events via replay. It is never persisted or mutated directly.
* `createdAt` is set once from the first event's timestamp and never changes.
* `invalidTransitions` field exists in `SessionState` but is never incremented by `DefaultSessionReducer` — it is always 0.
* `SessionFailedEvent` and `StageFailedEvent` both map to FAILED status — there is no distinction in the session state between session-level and stage-level failure.

== PlantUML diagram

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




== known issues

* `SessionState.invalidTransitions` is declared but never written by `DefaultSessionReducer`. It is always 0. Either it is a placeholder for future use or dead code.
* `TransitionResult` is defined but unused within the module — no callers in the current codebase reference it.

== open questions

* None.
