414 lines
10 KiB
Markdown
414 lines
10 KiB
Markdown
# Correx Context Bootstrap
|
||
|
||
---
|
||
|
||
## 1. module architecture
|
||
|
||
```text
|
||
❯ tree -L 2
|
||
.
|
||
├── apps
|
||
│ ├── cli
|
||
│ ├── desktop
|
||
│ ├── server
|
||
│ └── worker
|
||
├── build
|
||
│ └── reports
|
||
├── build.gradle
|
||
├── core
|
||
│ ├── agents
|
||
│ ├── approvals
|
||
│ ├── artifacts
|
||
│ ├── config
|
||
│ ├── context
|
||
│ ├── events
|
||
│ ├── inference
|
||
│ ├── kernel
|
||
│ ├── observability
|
||
│ ├── policies
|
||
│ ├── router
|
||
│ ├── sessions
|
||
│ ├── stages
|
||
│ ├── tools
|
||
│ ├── transitions
|
||
│ └── validation
|
||
├── detekt.yml
|
||
├── docs
|
||
│ ├── architecture
|
||
│ ├── configs
|
||
│ ├── decisions
|
||
│ ├── des-doc-v0.1.md
|
||
│ ├── epics.md
|
||
│ ├── events
|
||
│ ├── example.yaml
|
||
│ ├── lang-framewok-missing-pieces.md
|
||
│ ├── modules
|
||
│ ├── plugins
|
||
│ ├── spec-v0.1.md
|
||
│ ├── structure.md
|
||
│ ├── threat_model
|
||
│ └── transitions
|
||
├── epics
|
||
│ ├── epic-1.5.md
|
||
│ ├── epic-1.md
|
||
│ ├── epic-2.md
|
||
│ └── epic-3.md
|
||
├── examples
|
||
│ ├── configs
|
||
│ ├── plugins
|
||
│ ├── stages
|
||
│ └── workflows
|
||
├── frontend
|
||
│ ├── src
|
||
│ └── static
|
||
├── gradle
|
||
│ └── wrapper
|
||
├── gradle.properties
|
||
├── gradlew
|
||
├── gradlew.bat
|
||
├── infrastructure
|
||
│ ├── inference
|
||
│ ├── persistence
|
||
│ ├── scheduler
|
||
│ ├── security
|
||
│ ├── telemetry
|
||
│ └── tools
|
||
├── interfaces
|
||
│ ├── api
|
||
│ ├── cli
|
||
│ └── sdk
|
||
├── plugins
|
||
│ ├── compressors
|
||
│ ├── policies
|
||
│ ├── providers
|
||
│ ├── stages
|
||
│ ├── tools
|
||
│ ├── transitions
|
||
│ └── validators
|
||
├── settings.gradle
|
||
└── testing
|
||
├── approvals
|
||
├── contracts
|
||
├── deterministic
|
||
├── fixtures
|
||
├── integration
|
||
├── projections
|
||
├── replay
|
||
└── transitions
|
||
```
|
||
|
||
---
|
||
|
||
## dependency rules (important)
|
||
|
||
* core modules → no infrastructure dependencies
|
||
|
||
* infrastructure → implements core interfaces
|
||
|
||
* testing/contracts → defines reusable invariants
|
||
|
||
* core/events = event system foundation
|
||
|
||
* core/sessions = deterministic event-sourced lifecycle projection layer
|
||
|
||
* core/transitions = deterministic workflow graph evaluation engine
|
||
|
||
* projections are domain-specific folds over event streams
|
||
|
||
* replay engine stays generic and domain-agnostic
|
||
|
||
* transition engine is pure evaluation logic (no orchestration)
|
||
|
||
---
|
||
|
||
## 2. current epic
|
||
|
||
```text
|
||
Epic 4: Validation pipeline
|
||
|
||
goal: deterministic multi-layer validation system for workflows, sessions, and transitions
|
||
|
||
deliverables:
|
||
|
||
- routing validation layer (graph + transition correctness)
|
||
- schema validation layer (event + config integrity)
|
||
- semantic validation hooks (domain rules without execution coupling)
|
||
- validation pipeline executor (ordered deterministic execution)
|
||
- approval trigger integration point (event-based, no direct execution coupling)
|
||
|
||
dependencies:
|
||
|
||
- Epic 1 (event system + replay foundation)
|
||
- Epic 2 (session projection layer)
|
||
- Epic 3 (workflow transition engine)
|
||
```
|
||
|
||
---
|
||
|
||
## 3. implementation state
|
||
|
||
```text
|
||
READY TO START
|
||
|
||
Completed before Epic 4:
|
||
|
||
- append-only EventStore with strict ordering guarantees
|
||
- polymorphic EventPayload serialization (kotlinx.serialization)
|
||
- deterministic replay infrastructure (EventReplayer + Projection fold model)
|
||
- session lifecycle projection (SessionProjector + SessionReducer)
|
||
- FSM abstraction replaced by deterministic reducer semantics
|
||
- workflow graph model (WorkflowGraph, TransitionEdge, StageId)
|
||
- deterministic transition evaluation engine (resolver + condition evaluation)
|
||
- cycle detection + canonicalization (DFS-based graph analysis)
|
||
- stage execution contract (execution isolated from orchestration)
|
||
- workflow execution events integrated into event stream
|
||
- session ↔ transition integration via event-driven projection model
|
||
- full replay determinism across session + workflow execution
|
||
|
||
```
|
||
|
||
---
|
||
|
||
## 4. key design decisions (do not violate)
|
||
|
||
```text
|
||
- EventPayload uses kotlinx.serialization polymorphic serialization
|
||
- EventStore is append-only per session stream
|
||
- sequence is strictly monotonic per session
|
||
- projections are pure functions: (state, event) -> new state
|
||
- replay must be deterministic across implementations
|
||
- core/events contains replay infrastructure only
|
||
|
||
- core/sessions contains event-driven lifecycle projection (no FSM coupling)
|
||
- SessionProjector is a deterministic reducer over StoredEvent stream
|
||
- SessionReducer replaces FSM semantics (no imperative state transitions)
|
||
|
||
- core/transitions is a pure workflow graph evaluation engine
|
||
- transitions do NOT execute runtime logic
|
||
- transitions do NOT schedule or orchestrate execution
|
||
- transitions only emit execution-relevant events
|
||
|
||
- workflow graph is immutable and unordered (Set-based topology)
|
||
- determinism is enforced by resolver, not input structure
|
||
|
||
- cycle detection is analysis-only in Epic 3 (no runtime policy enforcement yet)
|
||
|
||
- Event stream is the single source of truth for all system state
|
||
|
||
- projections MUST NOT perform IO, side effects, or runtime clock access
|
||
- createdAt/updatedAt derived strictly from EventMetadata timestamps
|
||
|
||
- same event stream MUST always reconstruct identical system state
|
||
```
|
||
|
||
---
|
||
|
||
## 5. current invariants
|
||
|
||
```text
|
||
- no mutation outside store/projection boundaries
|
||
- ordering is enforced at EventStore level
|
||
- idempotency enforced by eventId
|
||
- replay must be deterministic for identical event streams
|
||
|
||
- projections are stateless deterministic reducers
|
||
- projections MUST NOT depend on external mutable state
|
||
- projections MUST NOT perform side effects
|
||
|
||
- event sequence is authoritative ordering source
|
||
- event stream is the only system truth source
|
||
|
||
- session state is fully derived from replay
|
||
- session state is never persisted directly
|
||
|
||
- workflow execution is event-driven and replay-safe
|
||
- transition evaluation must be deterministic and context-bound
|
||
|
||
- workflow topology is independent from execution semantics
|
||
- graph structure does not define execution order
|
||
|
||
- cycles are structurally allowed but not yet policy-governed
|
||
```
|
||
|
||
---
|
||
|
||
## 6. current task
|
||
|
||
```text
|
||
Epic 4 implementation:
|
||
|
||
Design and implement deterministic validation pipeline over workflow + session + transition layers.
|
||
|
||
Focus areas:
|
||
|
||
1. validate workflow graph correctness (structural + cycle awareness hooks)
|
||
2. validate transition definitions (conditions + reachability consistency)
|
||
3. validate session-event consistency against workflow semantics
|
||
4. introduce ordered validation pipeline executor
|
||
5. define extension points for semantic validation rules
|
||
6. ensure validation is replay-safe and deterministic
|
||
|
||
Important constraint:
|
||
validation MUST NOT mutate state or trigger execution
|
||
it only produces deterministic validation results over existing models
|
||
```
|
||
|
||
---
|
||
|
||
## 7. relevant code
|
||
|
||
no new core code required at bootstrap level beyond existing:
|
||
|
||
```kotlin
|
||
data class WorkflowGraph(
|
||
val stages: Set<StageId>,
|
||
val transitions: Set<TransitionEdge>,
|
||
val start: StageId
|
||
) {
|
||
init {
|
||
require(start in stages) {
|
||
"start stage must exist in stages"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
```kotlin
|
||
class DefaultTransitionResolver(
|
||
private val evaluator: TransitionConditionEvaluator
|
||
) : TransitionResolver {
|
||
|
||
override fun resolve(
|
||
graph: WorkflowGraph,
|
||
context: EvaluationContext
|
||
): TransitionDecision {
|
||
val outgoing = graph.transitions
|
||
.asSequence()
|
||
.filter { it.from == context.currentStage }
|
||
.sortedWith(TransitionOrdering.comparator)
|
||
.toList()
|
||
|
||
for (edge in outgoing) {
|
||
val result = evaluator.evaluate(edge.condition, context)
|
||
if (result) {
|
||
return TransitionDecision.Move(
|
||
transitionId = edge.id,
|
||
to = edge.to
|
||
)
|
||
}
|
||
}
|
||
return TransitionDecision.Stay
|
||
}
|
||
}
|
||
```
|
||
```kotlin
|
||
class SessionProjector(
|
||
private val reducer: SessionReducer
|
||
) : Projection<SessionState> {
|
||
|
||
override fun initial(): SessionState =
|
||
SessionState(
|
||
status = SessionStatus.CREATED
|
||
)
|
||
|
||
override fun apply(
|
||
state: SessionState,
|
||
event: StoredEvent
|
||
): SessionState = reducer.reduce(state, event)
|
||
}
|
||
```
|
||
|
||
```kotlin
|
||
class DefaultSessionReducer : SessionReducer {
|
||
|
||
override fun reduce(
|
||
state: SessionState,
|
||
event: StoredEvent
|
||
): SessionState {
|
||
|
||
val payload = event.payload
|
||
|
||
val newStatus = when (payload) {
|
||
|
||
is SessionStartedEvent ->
|
||
SessionStatus.ACTIVE
|
||
|
||
is SessionPausedEvent ->
|
||
SessionStatus.PAUSED
|
||
|
||
is SessionResumedEvent ->
|
||
SessionStatus.ACTIVE
|
||
|
||
is SessionCompletedEvent ->
|
||
SessionStatus.COMPLETED
|
||
|
||
is SessionFailedEvent,
|
||
is StageFailedEvent ->
|
||
SessionStatus.FAILED
|
||
|
||
is StageStartedEvent,
|
||
is StageCompletedEvent,
|
||
is TransitionExecutedEvent ->
|
||
SessionStatus.ACTIVE
|
||
|
||
else ->
|
||
state.status
|
||
}
|
||
|
||
val createdAt = state.createdAt
|
||
?: event.metadata.timestamp
|
||
|
||
return state.copy(
|
||
status = newStatus,
|
||
createdAt = createdAt,
|
||
updatedAt = event.metadata.timestamp
|
||
)
|
||
}
|
||
}
|
||
```
|
||
|
||
```kotlin
|
||
class DefaultEventReplayer<S>(
|
||
private val store: EventStore,
|
||
private val projection: Projection<S>
|
||
) : EventReplayer<S> {
|
||
|
||
override fun rebuild(sessionId: SessionId): S {
|
||
val events = store.read(sessionId)
|
||
|
||
return DefaultStateBuilder(projection)
|
||
.build(events)
|
||
}
|
||
}
|
||
```
|
||
|
||
```kotlin
|
||
internal class CycleExtractor(
|
||
private val adjacencyBuilder: DeterministicAdjacencyBuilder,
|
||
private val dfs: CycleDfs,
|
||
) {
|
||
|
||
fun extract(graph: WorkflowGraph): List<DetectedCycle> {
|
||
|
||
val adjacency = adjacencyBuilder.build(graph)
|
||
|
||
val state = mutableMapOf<StageId, VisitState>()
|
||
val path = mutableListOf<StageId>()
|
||
|
||
val cycles = mutableListOf<DetectedCycle>()
|
||
|
||
val sortedNodes = graph.stages
|
||
.sortedBy { it.value }
|
||
|
||
for (node in sortedNodes) {
|
||
if (state[node] == null) {
|
||
dfs.dfs(node, adjacency, state, path, cycles)
|
||
}
|
||
}
|
||
|
||
return canonicalize(cycles)
|
||
}
|
||
}
|
||
```
|
||
---
|