docs: add module documentation in AsciiDoc with PlantUML diagrams

Generated by per-group subagents covering all 52 modules across:
core (18), infrastructure (10), apps (5), interfaces (3),
plugins (7), testing (9)

Documentation format:
- AsciiDoc (.adoc) files in docs/modules/<group>/
- PlantUML (.puml) files in docs/diagrams/
- .adoc files reference diagrams via include:: directives

Each doc covers: purpose, responsibilities, non-responsibilities,
key types, event flow, integration points, invariants, PlantUML
diagram, known issues, and open questions.
This commit is contained in:
2026-05-26 16:59:21 +04:00
parent eadf23c945
commit 9734eec63c
103 changed files with 6045 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
= test-approvals
== purpose
Tests for the approval subsystem: tier immutability, grant semantics, mode-based approval, approval engine edge cases, and the decoupling of approval decisions from tool execution.
== responsibilities
* Verify that tier levels are immutable and cannot be downgraded
* Test grant semantics — who can approve what at which tier
* Test mode-based approval (auto-approve, prompt, deny) behavior
* Test approval engine edge cases (concurrent requests, expired requests, missing state)
* Verify that approval decisions do not couple to tool execution (decoupling invariant)
* Test the `ApprovalTrigger` mechanism for policy-driven approval initiation
== non-responsibilities
* Does not test the `ApprovalCoordinator` server module (tested via `:apps:server`'s own tests)
* Does not test the WebSocket approval flow end-to-end
* Does not test the approval UI components
== key types
=== TierImmutabilityTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that once a tier level is set, it cannot be modified — tier is an immutable value object.
=== GrantSemanticsTest
* **kind**: JUnit 5 test class
* **purpose**: Tests that the approval engine correctly grants or denies requests based on tier of the approving entity vs. tier of the request.
=== ModeApprovalTest
* **kind**: JUnit 5 test class
* **purpose**: Tests approval mode behavior: auto-approve mode skips user interaction, prompt mode requires explicit decision, deny mode rejects everything.
=== ApprovalEngineEdgeCasesTest
* **kind**: JUnit 5 test class
* **purpose**: Tests edge cases in the approval engine: duplicate request IDs, expired timestamps, null fields, concurrent resolution.
=== NoExecutionCouplingTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that approval decisions are evaluated independently of tool execution — rejecting an approval does not require understanding the tool's execution plan.
=== ApprovalTriggerTest
* **kind**: JUnit 5 test class
* **purpose**: Tests the approval trigger mechanism — which actions/conditions should trigger an approval request.
== event flow
*inbound:* Constructed `DomainApprovalRequest` and `DomainApprovalDecision` instances.
*outbound:* Assertions on approval state (approved, rejected, pending) and event outcomes.
== integration points
* `:core:approvals` — `DefaultApprovalEngine`, `ApprovalEngine`, `Tier`, `ApprovalOutcome`, `ApprovalStatus`, approval model types
* `:core:events` — approval-related event payload types
* `:testing:fixtures` — `ApprovalFixtures`
== invariants
* Tier levels are immutable — once assigned to a request or tool, the tier cannot change
* Policy denial is absolute — an approval cannot override a policy denial
* Approval decisions are independent of tool execution details
* Duplicate resolution of the same request is idempotent
== PlantUML diagram
[plantuml, test-approvals, "png"]
----
include::../../diagrams/test-approvals.puml[]
----
== known issues
* `ApprovalTriggerTest` behavior depends on how triggers are defined — if the trigger mechanism is not yet fully implemented, the test may be skeletal
== open questions
* Should there be tests for approval escalation (promoting a request to a higher tier if unresolved)?
* Should the approval engine support batch approval (approve N requests at once)?
+87
View File
@@ -0,0 +1,87 @@
= test-contracts
== purpose
Contract tests that validate the behavioral guarantees of core domain types. Ensures that value objects, model classes, and domain interfaces conform to their documented contracts across serialization, equality, and behavior.
== responsibilities
* Validate `WorkflowGraph`, `StageId`, `TransitionId` construction and invariants
* Test `InferenceProvider` contract (routing, health check, caching)
* Test `ValidationReport` contract and serialization
* Test `ContextSnapshot` construction and serialization
* Test orchestration model types (`OrchestrationConfig`, `WorkflowResult`, `StageOutcome`, `RetryPolicy`)
* Provide concurrent testing utilities (`ConcurrencyRunner`, `DeterministicBarrier`)
== non-responsibilities
* Does not test full integration between modules
* Does not test infrastructure adapters
* Does not test UI or application layers
== key types (test utilities)
=== ConcurrencyRunner
* **kind**: object
* **purpose**: Runs a block concurrently across N threads for M iterations using a `CountDownLatch` for synchronized start. Used for concurrency safety contract tests.
=== DeterministicBarrier
* **kind**: class
* **purpose**: Thin wrapper over `java.util.concurrent.CyclicBarrier` for synchronizing test threads at known points.
=== WorkflowGraphTest
* **kind**: JUnit 5 test class
* **purpose**: Tests `WorkflowGraph` construction, stage lookup, transition validation, and serialization round-trips.
=== InferenceProviderContractTest
* **kind**: JUnit 5 test class
* **purpose**: Defines the contract that all `InferenceProvider` implementations must satisfy (request handling, health check, capability reporting).
=== DefaultInferenceRouterTest / DefaultInferenceRouterCacheTest
* **kind**: JUnit 5 test classes
* **purpose**: Tests routing strategy, provider selection, and cache invalidation behavior.
=== EventsTest
* **kind**: JUnit 5 test class
* **purpose**: Tests event payload construction, metadata, and serialization contracts.
== event flow
*inbound:* Constructed domain objects and serialized JSON strings.
*outbound:* Assertions on equality, serialization round-trips, exception types, and behavioral contracts.
== integration points
* `:core:transitions` — `WorkflowGraph`, `StageConfig`, `TransitionEdge`
* `:core:inference` — `InferenceProvider`, `DefaultInferenceRouter`, `ProviderHealth`
* `:core:validation` — `ValidationReport`, validation model types
* `:core:events` — event payload types, `EventMetadata`
* `:core:context` — `ContextSnapshot`
* `:core:kernel` — orchestration config and result types
== invariants
* All serializable types survive a JSON encode/decode round-trip without data loss
* `StageId` and `TransitionId` are valid when non-blank
* `InferenceProvider.healthCheck()` never throws for healthy providers
== PlantUML diagram
[plantuml, test-contracts, "png"]
----
include::../../diagrams/test-contracts.puml[]
----
== known issues
* `ConcurrencyRunner` does not assert on thread safety — it only provides concurrent execution scaffolding; the test must supply assertions
* `DeterministicBarrier` does not have a timeout — a stalled test thread will hang indefinitely
== open questions
* Should contract tests be parameterized to run against all implementations of each interface?
* Should JSON schema validation be added to serialization contract tests?
@@ -0,0 +1,104 @@
= test-deterministic
== purpose
Tests verifying the determinism invariants of the Correx system: that event replay, context compression, approval decisions, router state, and validation pipelines produce identical results given identical inputs, independent of timing, execution order, or environment.
== responsibilities
* Verify that session replay is deterministic — identical event streams produce identical state
* Verify that context compression is deterministic — identical inputs produce identical compressed outputs
* Verify that the approval engine produces deterministic outcomes — same request + same config = same decision
* Verify that the router facade, projector, and reducer produce deterministic state
* Verify that the context pack builder produces deterministic results given the same inputs
* Verify token budget enforcement — requests exceeding the budget are rejected consistently
* Verify graph validation determinism — same graph produces same validation errors
* Verify validation pipeline short-circuit behavior — short-circuit outcomes are consistent and deterministic
== non-responsibilities
* Does not test performance or timing characteristics
* Does not test non-deterministic components (real inference providers, network I/O)
* Does not test application layers or UI
== key types
=== SessionReplayDeterminismTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that replaying the same event stream multiple times produces identical session state each time.
=== ContextCompressionDeterminismTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that the context compressor produces identical output when run multiple times with the same inputs and budget.
=== ApprovalEngineDeterminismTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that the approval engine returns the same decision given the same request and configuration across multiple invocations.
=== RouterFacadeTest
* **kind**: JUnit 5 test class
* **purpose**: Tests the router facade for deterministic behavior — identical user inputs produce identical router responses (when router is mocked).
=== RouterProjectorTest / RouterReducerTest / RouterContextBuilderTest
* **kind**: JUnit 5 test classes
* **purpose**: Test router projection, reduction, and context building determinism.
=== GraphValidatorTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that workflow graph validation produces consistent results for the same graph topology.
=== ValidationPipelineShortCircuitTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that validation pipeline short-circuiting behaves consistently — a fatal validator always stops the pipeline regardless of invocation order.
=== DefaultContextPackBuilderTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that the context pack builder produces identical context packs given identical input events and budgets.
=== TokenBudgetEnforcementTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that token budget enforcement is deterministic — requests over budget are consistently rejected or truncated.
== event flow
*inbound:* Constructed events, configurations, and inputs that are fed through the component under test multiple times.
*outbound:* Assertions that identical outputs are produced for each invocation.
== integration points
* `:core:sessions` — `DefaultSessionReducer`, `SessionProjector`, `EventReplayer`
* `:core:context` — `DefaultContextPackBuilder`, `DefaultContextCompressor`, `TokenBudget`
* `:core:approvals` — `DefaultApprovalEngine`
* `:core:router` — `RouterFacade`, `RouterProjector`, `RouterReducer`
* `:core:transitions` — `GraphValidator`
* `:core:validation` — `ValidationPipeline`, `Validator`
* `:testing:fixtures` — `EventFixtures`, `InferenceFixtures`, `ApprovalFixtures`, `ContextFixtures`
== invariants
* All projected state is deterministic — given the same event stream, the output is always identical
* All compression operations are deterministic — given the same entries and budget, the output is always the same
* The approval engine is deterministic — given the same input, the decision is always the same
* Token budget enforcement produces deterministic pass/fail outcomes per request
* Validation pipeline short-circuit is deterministic — the order of validators does not affect the short-circuit outcome
== PlantUML diagram
[plantuml, test-deterministic, "png"]
----
include::../../diagrams/test-deterministic.puml[]
----
== known issues
* Determinism tests rely on mocked components — they verify the mock's determinism, not the real runtime's
* Some tests may pass with mocks but fail with real implementations (e.g., real tokenizer adds nondeterminism)
== open questions
* Should there be a dedicated determinism test that runs against the full production stack (minus network)?
* Should CI run determinism tests with multiple seed values to increase coverage?
+113
View File
@@ -0,0 +1,113 @@
= test-fixtures
== purpose
Shared test fixture library providing reusable mock implementations, factory functions, and test doubles for all Correx testing modules. Reduces duplication and ensures consistent test setup across the project.
== responsibilities
* Provide factory functions for constructing `NewEvent`, `StoredEvent`, and domain events with minimal boilerplate
* Provide mock inference provider (`MockInferenceProvider`) with configurable behavior (fixed response, delay, forced failure)
* Provide mock tokenizer (`MockTokenizer`) with deterministic character-based token counting
* Provide test tool implementations (`EchoTool`, `FailingTool`, `RejectedTool`) at different tier levels
* Provide mock event replayers and session repositories for kernel tests
* Provide workflow graph fixtures (`simpleGraph`, `threeStageGraph`)
* Provide approval fixtures (approval request factory, cycle policy validator)
* Provide cycle detection fixtures
* Provide context compression fixtures (no-op compressor, simple builder)
* Provide inference fixtures (fixed router, failing router, context pack, generation config)
== non-responsibilities
* Does not contain any test assertions or test cases (except `MockToolsTest` which validates the tools themselves)
* Does not provide mocks for infrastructure adapters (SQLite, HTTP, etc.)
* Does not provide full in-memory event store replacement
== key types
=== EventFixtures
* **kind**: object
* **purpose**: Factory methods `newEvent()` and `stored()` that produce `NewEvent` and `StoredEvent` instances with sensible defaults. Reduces boilerplate in event-sourcing tests.
=== MockInferenceProvider
* **kind**: class
* **purpose**: Configurable fake `InferenceProvider`. Three mutually exclusive modes: `fixedResponse` (returns immediately), `artificialDelayMs` (suspends before responding), `forcedFailure` (throws). Tracks `inferCallCount`.
=== MockTokenizer
* **kind**: class
* **purpose**: Character-based `Tokenizer` approximation: 1 token ≈ 4 characters. Deterministic and not model-accurate. Suitable for contract and budget tests.
=== EchoTool / FailingTool / RejectedTool
* **kind**: classes implementing `Tool`
* **purpose**: Test tools at tiers T0, T2, and T4 respectively. `EchoTool` validates successfully, `FailingTool` fails validation, `RejectedTool` validates but is configured for rejection.
=== MockEventReplayer / MockSessionRepository
* **kind**: classes
* **purpose**: Test doubles for kernel module dependencies. Return pre-configured or default states by session ID.
=== InferenceFixtures
* **kind**: object
* **purpose**: Factory methods for `InferenceProvider`, `InferenceRouter`, `InferenceRequest`, `InferenceResponse`, `ContextPack`, `GenerationConfig`.
=== ApprovalFixtures
* **kind**: top-level functions
* **purpose**: `request()` factory for `DomainApprovalRequest`; `cyclePolicyMissingValidator()` for validation pipeline tests.
=== WorkflowFixtures
* **kind**: object
* **purpose**: `simpleGraph()` factory creating a two-stage A->B workflow graph.
=== TransitionFixtures
* **kind**: object
* **purpose**: `alwaysTrueEvaluator()`, `simpleResolver()`, `threeStageGraph()` for transition resolution tests.
=== CycleFixtures
* **kind**: object
* **purpose**: `simpleCycle()` for cycle detection tests.
=== ContextFixtures
* **kind**: object
* **purpose**: `simpleCompressor()` (no-op) and `simpleBuilder()` for context builder tests.
== event flow
Not applicable — this module provides construction helpers, not event processing.
== integration points
* `:core:events` — `EventPayload`, `NewEvent`, `StoredEvent`, `EventMetadata`
* `:core:inference` — `InferenceProvider`, `InferenceRouter`, `InferenceRequest`, `InferenceResponse`, `Tokenizer`
* `:core:tools` — `Tool` interface, `Tier`
* `:core:approvals` — `DomainApprovalRequest`, domain approval types
* `:core:transitions` — `WorkflowGraph`, `TransitionResolver`, `TransitionConditionEvaluator`
* `:core:context` — `ContextPack`, `ContextCompressor`, `DefaultContextPackBuilder`
* `:core:sessions` — `SessionState`, `EventReplayer`
* `:core:kernel` — `OrchestrationState`
* `:core:validation` — `Validator`, validation model types
== invariants
* `MockTokenizer.tokenize("")` returns empty list; `countTokens("")` returns 0
* `MockTokenizer` uses 4-char chunking — `countTokens("abcd")` = 1, `countTokens("abcde")` = 2
* `EventFixtures.stored()` defaults to `sessionSequence = sequence` (1:1 mapping)
* `MockInferenceProvider.inferCallCount` increments atomically per call
== PlantUML diagram
[plantuml, test-fixtures, "png"]
----
include::../../diagrams/test-fixtures.puml[]
----
== known issues
* `MockTokenizer` is not model-accurate — tests using it for token budgets may pass with the mock but fail with a real tokenizer
* `EventFixtures.stored()` sets `sessionSequence = sequence` by default, which may not match production behavior where sequences diverge after resets or gaps
== open questions
* Should `MockInferenceProvider` support streaming response simulation?
* Should the fixtures include an in-memory event store implementation for tests?
@@ -0,0 +1,69 @@
= test-integration
== purpose
Integration tests that exercise end-to-end workflows spanning multiple core modules and infrastructure components. Verifies that components wire together correctly and produce the expected outcomes across subsystem boundaries.
== responsibilities
* Test the `SessionOrchestrator` end-to-end: from session start through stage transitions, tool execution, and completion
* Test the `ValidationPipeline` end-to-end: from payload submission through all validators to final validation report
* Verify that events flow correctly between subsystems (reducer -> store -> projector -> repository)
== non-responsibilities
* Does not test with a real SQLite/Postgres event store (uses in-memory store)
* Does not test real inference providers or tool executors
* Does not test UI or application layers
* Does not test network-level integration (HTTP, WebSocket)
== key types
=== SessionOrchestratorIntegrationTest
* **kind**: JUnit 5 test class
* **purpose**: Tests full orchestration lifecycle: creates a `WorkflowGraph`, starts a session via `DefaultSessionOrchestrator`, verifies stage progression, tool invocation events, and session completion.
=== ValidationPipelineIntegrationTest
* **kind**: JUnit 5 test class
* **purpose**: Tests the validation pipeline with multiple validators, verifying that each validator is invoked and that the aggregated validation report is correct.
== event flow
*inbound:* Constructed `NewEvent` instances and orchestration commands submitted to the orchestrator or validation pipeline.
*outbound:* Assertions on `StoredEvent` sequences emitted to the event store, final state from projections, and validation reports.
== integration points
* `:core:kernel` — `DefaultSessionOrchestrator`, `OrchestrationConfig`, `OrchestratorEngines`
* `:core:events` — `EventStore`, event payload types
* `:core:validation` — `ValidationPipeline`, `Validator`, validation model types
* `:core:sessions` — `DefaultSessionRepository`, `SessionProjector`
* `:core:transitions` — `DefaultTransitionResolver`, `WorkflowGraph`
* `:testing:fixtures` — mock providers, tools, compressors, repositories, and event replayers
== invariants
* Orchestrator follows the configured workflow graph — stages execute in declared order
* The event store contains all events produced during orchestration
* Validation pipeline evaluates all registered validators
== PlantUML diagram
[plantuml, test-integration, "png"]
----
include::../../diagrams/test-integration.puml[]
----
== known issues
* Tests use mocked engines, not real inference or tool execution — real end-to-end behavior is not validated
* In-memory event store may not surface ordering or persistence bugs present in production stores
== open questions
* Should integration tests include a real SQLite event store to verify write/read ordering?
* Should there be a separate integration test for the server module's HTTP and WebSocket routing?
+72
View File
@@ -0,0 +1,72 @@
= test-kernel
== purpose
Tests for the core kernel orchestration logic: retry coordination and inference replay. Verifies that the `DefaultRetryCoordinator` correctly manages retry attempts and that inference events replay to the correct provider state.
== responsibilities
* Test `DefaultRetryCoordinator` retry policy enforcement (max attempts, backoff, exhaustion)
* Test `ReplayInferenceProvider` to ensure inference events replay correctly through the inference projector
== non-responsibilities
* Does not test the full session orchestrator lifecycle
* Does not test infrastructure retry mechanisms (e.g., HTTP retry)
* Does not test UI or application layers
== key types
=== MockOrchestrationEventReplayer
* **kind**: class
* **purpose**: Test double implementing `EventReplayer<OrchestrationState>`. Returns pre-configured states by session ID; defaults to `RUNNING` with `stage-1`.
=== MockSessionEventReplayer
* **kind**: class
* **purpose**: Test double implementing `EventReplayer<SessionState>`. Returns pre-configured states by session ID; defaults to `ACTIVE`.
=== DefaultRetryCoordinatorTest
* **kind**: JUnit 5 test class
* **purpose**: Tests retry coordination logic — max retries, exponential backoff duration, retry exhaustion behavior.
=== ReplayInferenceProviderTest
* **kind**: JUnit 5 test class
* **purpose**: Tests that inference events streamed through the replay mechanism produce correct `InferenceState`.
== event flow
*inbound:* Programmatically constructed events and state objects injected into mock replayers.
*outbound:* Assertions on retry coordinator state (attempt count, delay values) and replayed inference state.
== integration points
* `:core:kernel` — `DefaultRetryCoordinator`, orchestration state and event types
* `:core:inference` — `InferenceProjector`, `InferenceState`, `InferenceRepository`
* `:core:sessions` — `SessionState`, `SessionStatus`
* `:core:events` — event payload types, `EventReplayer` interface
* `:testing:fixtures` — mock replayers and state builders
== invariants
* Retry coordinator respects max attempt limits — exceeding them marks the operation as exhausted
* Event replay produces the same inference state regardless of the number of replay passes
== PlantUML diagram
[plantuml, test-kernel, "png"]
----
include::../../diagrams/test-kernel.puml[]
----
== known issues
* Mock replayers provide pre-canned states rather than true event-sourced replay — tests do not verify the replay machinery itself, only the components that consume replay output
== open questions
* Should the tests also cover the `DefaultRetryCoordinator` integration with the event store?
* Should there be a full `EventStore` + `RetryCoordinator` integration test here?
@@ -0,0 +1,98 @@
= test-projections
== purpose
Unit tests for the event-sourced projection and reducer components. Verifies that each projector correctly rebuilds state from a sequence of events, and that reducers produce the correct state transitions for each event type.
== responsibilities
* Test `DefaultSessionReducer` and `SessionProjector` — session lifecycle state transitions
* Test `DefaultOrchestrationReducer` and `OrchestrationProjector` — orchestration state transitions
* Test `InferenceReducer` and `InferenceProjector` — inference state tracking
* Test `DefaultContextReducer` and `ContextProjector` — context state tracking
* Test `ArtifactReducer` — artifact metadata state tracking
* Test `RouterReducer` and `RouterProjector` — router state tracking
== non-responsibilities
* Does not test live event streaming or real-time event processing
* Does not test the event store write path
* Does not test the `SessionOrchestrator` or higher-level orchestration logic
* Does not test application layers
== key types
=== DefaultSessionReducerTest
* **kind**: JUnit 5 test class
* **purpose**: Tests `DefaultSessionReducer` with session lifecycle events (start, pause, resume, fail, complete). Verifies status transitions and state field updates.
=== SessionProjectorTest
* **kind**: JUnit 5 test class
* **purpose**: Tests `SessionProjector` by feeding sequences of events through the projector and verifying the final `SessionState`.
=== OrchestrationReducerTest
* **kind**: JUnit 5 test class
* **purpose**: Tests `DefaultOrchestrationReducer` with orchestration events (stage transitions, approvals, retries). Verifies state machine transitions.
=== OrchestrationProjectorTest
* **kind**: JUnit 5 test class
* **purpose**: Tests `OrchestrationProjector` by replaying orchestration event sequences and validating the final `OrchestrationState`.
=== InferenceReducerTest / InferenceProjectorTest
* **kind**: JUnit 5 test classes
* **purpose**: Tests inference event reduction and projection — request lifecycle, completion, timeout, and failure.
=== ContextReducerTest / ContextProjectorTest
* **kind**: JUnit 5 test classes
* **purpose**: Tests context pack building and compression through the projection pipeline.
=== RouterReducerTest / RouterProjectorTest
* **kind**: JUnit 5 test classes
* **purpose**: Tests router state projection — tracks router interaction history and state.
=== ArtifactReducerTest
* **kind**: JUnit 5 test class
* **purpose**: Tests artifact metadata reduction from tool execution events.
== event flow
*inbound:* Constructed `StoredEvent` sequences representing the full lifecycle of a session or subsystem.
*outbound:* Assertions on the final projected `*State` object, comparing field-by-field against expected values.
== integration points
* `:core:sessions` — `DefaultSessionReducer`, `SessionProjector`, `SessionState`
* `:core:kernel` — `DefaultOrchestrationReducer`, `OrchestrationProjector`, `OrchestrationState`
* `:core:inference` — `InferenceReducer`, `InferenceProjector`, `InferenceState`
* `:core:context` — `DefaultContextReducer`, `ContextProjector`, context state types
* `:core:artifacts` — `ArtifactReducer`
* `:core:router` — `RouterReducer`, `RouterProjector`
* `:core:events` — `StoredEvent`, all event payload types
* `:testing:fixtures` — `EventFixtures`
== invariants
* Each projector produces the same final state given the same event sequence, regardless of intermediate projections
* Reducers only modify state via `state.copy(...)` — no side effects
* Unknown event types in a reducer return the state unchanged
== PlantUML diagram
[plantuml, test-projections, "png"]
----
include::../../diagrams/test-projections.puml[]
----
== known issues
* Tests may not cover all edge cases (empty event lists, out-of-order events, duplicate events)
* Event sequences are constructed manually and may not reflect real-world ordering guarantees from the event store
== open questions
* Should projection tests verify behavior with interleaved events from multiple sessions?
* Should there be property-based tests (e.g., `kotlinx.kotest.properties`) verifying the projector invariants across random event sequences?
+80
View File
@@ -0,0 +1,80 @@
= test-replay
== purpose
Tests that verify the event replay mechanism produces identical state regardless of when or how events are replayed. Ensures that projections are deterministic and that the full event stream can be reconstructed faithfully.
== responsibilities
* Verify that session state rebuilt from replay matches expected outcomes
* Test replay produces correct results for empty event streams (no events)
* Validate that transition-driven workflows replay to the correct final state
* Verify that approval decisions survive replay and produce correct state
* Test that validation pipeline events are correctly replayed and ordered
== non-responsibilities
* Does not test live streaming or real-time event processing
* Does not test performance characteristics of replay (only correctness)
* Does not test cross-session replay interactions
== key types
=== SessionReplayTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that full event streams replayed from the beginning produce the expected session state.
=== SessionEmptyReplayTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies replay behavior when no events have been recorded — the empty state case.
=== TransitionReplayIntegrationTest
* **kind**: JUnit 5 test class
* **purpose**: Tests that a multi-stage workflow with transitions replays to the correct final orchestration state.
=== ApprovalReplayTest
* **kind**: JUnit 5 test class
* **purpose**: Verifies that approval request and decision events are correctly replayed and produce the expected approval state.
=== ValidationReplayTest
* **kind**: JUnit 5 test class
* **purpose**: Tests replay of validation events through the validation pipeline.
== event flow
*inbound:* Constructed `NewEvent` instances simulating domain events (session start, stage transitions, approvals, tool invocations).
*outbound:* Assertions against rebuilt `SessionState`, `OrchestrationState`, `ApprovalState`.
== integration points
* `:core:sessions` — `DefaultSessionReducer`, `SessionProjector`, `SessionState`
* `:core:kernel` — `OrchestrationState`, `DefaultOrchestrationReducer`
* `:core:approvals` — `ApprovalState`, `DefaultApprovalReducer`, `ApprovalProjector`
* `:core:validation` — `ValidationPipeline`
* `:core:events` — `EventStore`, `StoredEvent`, event payload types
* `:testing:fixtures` — `EventFixtures`, `ApprovalFixtures`, `WorkflowFixtures`
== invariants
* Replay from an empty event store produces initial (empty/default) state
* Replay from a full event log produces the same state as when the events were first applied
* Approval decisions are persisted as events and survive replay
== PlantUML diagram
[plantuml, test-replay, "png"]
----
include::../../diagrams/test-replay.puml[]
----
== known issues
* Tests may rely on `InMemoryEventStore` which may not perfectly reproduce the behavior of the production SQLite-backed store
== open questions
* Should replay tests verify that replay is idempotent (replaying the same stream twice produces identical state)?
@@ -0,0 +1,64 @@
= test-transitions
== purpose
Tests for transition resolution and event serialization in the workflow graph. Verifies that the `DefaultTransitionResolver` correctly evaluates conditions and selects the next stage, and that transition events survive serialization round-trips.
== responsibilities
* Test `DefaultTransitionResolver` with various condition evaluators and workflow graph configurations
* Test `TransitionEvent` and related event types for serialization round-trip correctness
== non-responsibilities
* Does not test the orchestration engine's integration with transitions
* Does not test complex condition logic (uses simple always-true evaluators)
* Does not test cycle detection or graph validation (covered by `testing:deterministic`)
== key types
=== DefaultTransitionResolverTest
* **kind**: JUnit 5 test class
* **purpose**: Tests that `DefaultTransitionResolver` correctly resolves the next stage given a `WorkflowGraph`, current stage, and condition evaluator. Tests linear chains, branching, and terminal stages.
=== TransitionEventSerializationTest
* **kind**: JUnit 5 test class
* **purpose**: Tests that `TransitionExecutedEvent` and related events serialize and deserialize correctly through `kotlinx.serialization`.
== event flow
*inbound:* Constructed `WorkflowGraph` instances and `TransitionConditionEvaluator` functions.
*outbound:* Assertions on resolved `TransitionResult` (next stage ID) and serialization round-trip fidelity.
== integration points
* `:core:transitions` — `DefaultTransitionResolver`, `TransitionResolver`, `WorkflowGraph`, `TransitionEdge`, `TransitionExecutedEvent`
* `:core:events` — event payload types, `StoredEvent`
* `:testing:fixtures` — `TransitionFixtures`, `WorkflowFixtures`
== invariants
* A terminal stage (no outgoing transitions) resolves to no next stage
* A stage with a single outgoing transition resolves to that target when the condition is true
* Transition events survive a full JSON encode/decode round-trip
== PlantUML diagram
[plantuml, test-transitions, "png"]
----
include::../../diagrams/test-transitions.puml[]
----
== known issues
* Tests only cover always-true conditions — no tests for conditional or guarded transitions
* `TransitionEventSerializationTest` uses serialization only, no deserialization context with the polymorphic `eventModule`
== open questions
* Should transition resolver tests cover multi-condition evaluation with AND/OR semantics?
* Should negative conditions (evaluate to false) be tested for fallback behavior?