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:
@@ -0,0 +1,84 @@
|
||||
= app-cli
|
||||
|
||||
== purpose
|
||||
|
||||
Command-line interface built with Clikt that connects to the Correx server to start, manage, and observe sessions. Designed for scripted/headless usage and non-interactive environments.
|
||||
|
||||
== responsibilities
|
||||
|
||||
* Start sessions by workflow ID via HTTP POST to the server
|
||||
* Attach to a session's WebSocket stream and relay lifecycle events (session, stage, tool, approval) to stdout
|
||||
* Prompt for approval decisions (approve / reject / steer) when a TTY is available
|
||||
* Support `--json` output for machine parsing and `--quiet` for error-only mode
|
||||
* List sessions, resume/cancel sessions, fetch session events
|
||||
* List inference providers and their health status
|
||||
* Send approval decisions to the server via HTTP
|
||||
|
||||
== non-responsibilities
|
||||
|
||||
* Does not maintain persistent state or local event cache
|
||||
* Does not contain domain orchestration logic
|
||||
* Does not perform inference or tool execution
|
||||
* Does not implement the approval engine — only forwards user decisions
|
||||
* Does not run a server
|
||||
|
||||
== key types
|
||||
|
||||
=== CorrexCli
|
||||
* **kind**: `CliktCommand` (root)
|
||||
* **purpose**: Top-level CLI command. Defines global flags `--json` and `--quiet`.
|
||||
|
||||
=== RunCommand
|
||||
* **kind**: `CliktCommand` (subcommand)
|
||||
* **purpose**: Starts a session for a workflow and streams events to stdout. Supports `--workflow`, `--session`, `--auto-approve`, `--host`, `--port`.
|
||||
|
||||
=== CliWsClient
|
||||
* **kind**: class
|
||||
* **purpose**: Thin Ktor WebSocket client wrapping `HttpClient.webSocket`. Calls `onMessage` callback per frame; returns `false` from callback to close the stream.
|
||||
|
||||
=== SystemExitException
|
||||
* **kind**: exception
|
||||
* **purpose**: Propagates exit codes (0 = success, 1 = session failure, 2 = rejection in non-TTY mode).
|
||||
|
||||
=== RunContext / ApprovalContext
|
||||
* **kind**: internal data classes
|
||||
* **purpose**: Carry session ID, JSON flag, auto-approve flag, TTY status through the message handling loop.
|
||||
|
||||
== event flow
|
||||
|
||||
*inbound:* WebSocket frames from server encoded as JSON `ServerMessage` variants. The CLI decodes the `type` field from a raw `JsonObject` (not deserialized via `ProtocolSerializer`).
|
||||
|
||||
*outbound:* `ApprovalResponsePayload` sent as text frames on the WebSocket session.
|
||||
|
||||
== integration points
|
||||
|
||||
* `apps:cli` depends only on external libraries: Clikt (CLI framework) and Ktor client (HTTP + WebSockets)
|
||||
* Talks to the server at `http://{host}:{port}` via REST and `ws://{host}:{port}/sessions/{id}/stream`
|
||||
|
||||
== invariants
|
||||
|
||||
* `CliWsClient.sessionStream` breaks out of the frame loop when `onMessage` returns `false`
|
||||
* Auto-approve without a TTY is impossible — if `--auto-approve` is not set and no TTY, all approvals are rejected
|
||||
* Exit code 2 means the user rejected an approval in non-interactive mode
|
||||
* `RunCommand` creates one session and one WebSocket connection per invocation
|
||||
|
||||
== PlantUML diagram
|
||||
|
||||
[plantuml, app-cli, "png"]
|
||||
----
|
||||
include::../../diagrams/app-cli.puml[]
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
== known issues
|
||||
|
||||
* JSON output format is hand-constructed in `printLine()` rather than using a serializer — field ordering and escaping may diverge from `ServerMessage` serialization
|
||||
* `--session` (resume) acceptance on the server side is not yet implemented — the parameter is forwarded but the server's response is untested
|
||||
* `resolveDecision` blocks on stdin in TTY mode — no timeout mechanism for user input
|
||||
|
||||
== open questions
|
||||
|
||||
* Should session resume reconnect to an existing WebSocket stream or replay from the event store? Currently the parameter is accepted but behavior is undefined.
|
||||
* Should `RunCommand` support multiple sessions concurrently (e.g., `--watch` mode)?
|
||||
@@ -0,0 +1,54 @@
|
||||
= app-desktop
|
||||
|
||||
== purpose
|
||||
|
||||
Placeholder application module for a future desktop GUI application. Currently contains only a stub `main()` function that prints its identity and exits.
|
||||
|
||||
== responsibilities
|
||||
|
||||
* Declares the module structure and build configuration for a future desktop application
|
||||
* Prints a startup banner to stdout
|
||||
|
||||
== non-responsibilities
|
||||
|
||||
* Does not create any windows, UI components, or graphical interface
|
||||
* Does not contain any domain logic or infrastructure wiring
|
||||
* Does not connect to any Correx server
|
||||
|
||||
== key types
|
||||
|
||||
=== Main.kt
|
||||
* **kind**: top-level `main()` function
|
||||
* **purpose**: Placeholder entry point. Prints `"correx :: apps/desktop"` and exits.
|
||||
|
||||
== event flow
|
||||
|
||||
None. The module has no event interaction.
|
||||
|
||||
== integration points
|
||||
|
||||
* Depends only on `com.github.ajalt.clikt:clikt` in build configuration (no actual usage in source)
|
||||
|
||||
== invariants
|
||||
|
||||
* The module has no runtime state, invariants, or behavioral contracts beyond printing to stdout
|
||||
|
||||
== PlantUML diagram
|
||||
|
||||
[plantuml, app-desktop, "png"]
|
||||
----
|
||||
include::../../diagrams/app-desktop.puml[]
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
== known issues
|
||||
|
||||
* Not implemented — this is a structural placeholder with no functional code
|
||||
|
||||
== open questions
|
||||
|
||||
* Will the desktop app be built with Compose Multiplatform, JavaFX, or another UI toolkit?
|
||||
* Should the desktop app embed the Correx server, or connect to an existing server instance?
|
||||
* What is the relationship between desktop app and TUI — do they share components or are they independent implementations?
|
||||
@@ -0,0 +1,132 @@
|
||||
= app-server
|
||||
|
||||
== purpose
|
||||
|
||||
Ktor-based HTTP + WebSocket server that hosts the Correx orchestration runtime. Wires together core domain modules with infrastructure adapters to run, observe, and manage agentic workflow sessions.
|
||||
|
||||
== responsibilities
|
||||
|
||||
* Expose REST endpoints for session lifecycle (create, get, cancel, list events)
|
||||
* Expose REST endpoints for workflow discovery and provider health
|
||||
* Maintain long-lived WebSocket connections for per-session and global event streaming
|
||||
* Bridge domain events (`StoredEvent`) to wire protocol messages (`ServerMessage`)
|
||||
* Coordinate approval workflows: register WS clients, receive decisions, forward to orchestration
|
||||
* Log all emitted events via `LoggingEventStore`
|
||||
* Initialize and wire the full dependency graph: event store, repositories, engines, registries
|
||||
|
||||
== non-responsibilities
|
||||
|
||||
* Does not perform inference — delegates to `core:inference` via `InferenceRouter`
|
||||
* Does not execute tools — delegates to `infrastructure:tools` via `ToolExecutor`
|
||||
* Does not persist state beyond what the event store and artifact store provide
|
||||
* Does not handle TLS termination or authentication (no security middleware)
|
||||
* Does not provide horizontal scaling or clustering
|
||||
|
||||
== key types
|
||||
|
||||
=== ServerModule
|
||||
* **kind**: class
|
||||
* **purpose**: Top-level wiring container holding all server-scoped dependencies (orchestrator, event store, registries, repositories, coordinator). Creates an `ApprovalCoordinator` and subscribes to `ApprovalRequestedEvent` on `start()`.
|
||||
* **fields**: `moduleScope: CoroutineScope` — SupervisorJob-backed scope owned by the module; hosts the event subscription and approval timeout jobs.
|
||||
|
||||
=== Application.configureServer(module)
|
||||
* **kind**: extension function
|
||||
* **purpose**: Installs Ktor plugins (CallLogging, WebSockets, ContentNegotiation, StatusPages) and defines routing tree.
|
||||
|
||||
=== GlobalStreamHandler
|
||||
* **kind**: class
|
||||
* **purpose**: Handles `/stream` WebSocket connections. Sends initial snapshot (provider health + workflow list), then streams all events from `eventStore.subscribeAll()` via `SessionEventBridge` + `DomainEventMapper`.
|
||||
|
||||
=== SessionStreamHandler
|
||||
* **kind**: class
|
||||
* **purpose**: Handles `/sessions/{id}/stream` WebSocket connections. Replays events from optionally specified `lastEventId` cursor, then forwards incoming client messages (approval, cancel, chat input, ping) to the appropriate module component.
|
||||
|
||||
=== DomainEventMapper
|
||||
* **kind**: class
|
||||
* **purpose**: Maps domain `StoredEvent.payload` variants to protocol `ServerMessage` variants for WebSocket transmission.
|
||||
|
||||
=== SessionEventBridge
|
||||
* **kind**: class
|
||||
* **purpose**: Reconstructs snapshot state (orchestration status, pending approvals, recent events) for all active sessions from the event store. Used during global stream initialization.
|
||||
|
||||
=== ApprovalCoordinator
|
||||
* **kind**: class
|
||||
* **purpose**: Manages approval request routing: registers session and global WS clients, broadcasts `ApprovalRequired` messages, schedules timeout-based rejection, forwards `ApprovalResponse` decisions to the orchestrator.
|
||||
|
||||
=== ApprovalResponseMapper (internal)
|
||||
* **kind**: extension function
|
||||
* **purpose**: Translates `ClientMessage.ApprovalResponse` to domain `ApprovalDecision`.
|
||||
|
||||
=== LoggingEventStore
|
||||
* **kind**: class
|
||||
* **purpose**: Decorator over `EventStore` that logs all `append`/`appendAll` calls with session ID, event type, and outcome.
|
||||
|
||||
=== ProviderRegistry
|
||||
* **kind**: interface
|
||||
* **purpose**: Thin server-side adapter over `DefaultProviderRegistry` — exposes `listAll()` and `healthCheckAll()`.
|
||||
|
||||
=== WorkflowRegistry
|
||||
* **kind**: interface
|
||||
* **purpose**: Abstraction for discovering and loading `WorkflowGraph` definitions by ID.
|
||||
|
||||
=== FileSystemWorkflowRegistry
|
||||
* **kind**: class
|
||||
* **purpose**: Implements `WorkflowRegistry` by scanning `~/.config/correx/workflows/*.toml` lazily. Caches loaded graphs.
|
||||
|
||||
=== ServerMessage / ClientMessage / Dtos
|
||||
* **kind**: sealed interfaces / data classes
|
||||
* **purpose**: Wire protocol types. `ServerMessage` has two marker subtypes: `SessionMessage` (event-derived, carries sequence cursors) and `NonEventMessage` (control messages, no cursors).
|
||||
|
||||
== event flow
|
||||
|
||||
*inbound:* REST requests (POST/DELETE sessions, GET status/workflows/providers) and WebSocket `ClientMessage` frames (approval responses, cancel, chat input, ping).
|
||||
|
||||
*outbound:* WebSocket `ServerMessage` frames from `DomainEventMapper` (session lifecycle, stage lifecycle, tool lifecycle, inference lifecycle, approval requests). Also HTTP JSON responses for REST endpoints.
|
||||
|
||||
== integration points
|
||||
|
||||
* `:core:kernel` — `DefaultSessionOrchestrator` for running sessions, `ApprovalGateway` for injecting decisions
|
||||
* `:core:events` — `EventStore`, `StoredEvent`, all event payload types
|
||||
* `:core:approvals` — `DefaultApprovalEngine`, `Tier`, domain approval types
|
||||
* `:core:sessions` — `DefaultSessionRepository`, `DefaultSessionReducer`, `SessionProjector`
|
||||
* `:core:inference` — `DefaultInferenceRouter`, `InferenceProjector`, `InferenceRepository`
|
||||
* `:core:transitions` — `DefaultTransitionResolver`, `WorkflowGraph`
|
||||
* `:core:context` — `DefaultContextPackBuilder`, `DefaultContextCompressor`
|
||||
* `:core:validation` — `ValidationPipeline`, `ArtifactPayloadValidator`
|
||||
* `:core:risk` — `DefaultRiskAssessor`
|
||||
* `:core:router` — `RouterFacade`, `RouterConfig`
|
||||
* `:core:tools` — `ToolRegistry`
|
||||
* `:core:config` — `ConfigLoader`, `ApprovalConfig`
|
||||
* `:infrastructure:*` — providers, tool executor, workflow loader, artifact repository, approval repository, persistence
|
||||
|
||||
== invariants
|
||||
|
||||
* `ServerModule.start()` is idempotent — calling it twice is a no-op for the second call (`subscriptionJob` guard)
|
||||
* Event subscription is established BEFORE `replaySnapshot()` reads `lastGlobalSequence` to prevent missed events
|
||||
* Orchestrator is launched in `module.moduleScope` after protocol messages are delivered (ensures no silent data loss on disconnected WS)
|
||||
* Approval timeout is scheduled per-request; `resolved` map prevents double-resolution
|
||||
* `DomainEventMapper` returns `null` for unmapped payload types, which are silently dropped from the stream
|
||||
|
||||
== PlantUML diagram
|
||||
|
||||
[plantuml, app-server, "png"]
|
||||
----
|
||||
include::../../diagrams/app-server.puml[]
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
== known issues
|
||||
|
||||
* `GET /sessions` returns `emptyList()` unconditionally — no server-side session listing is implemented
|
||||
* `SessionStreamHandler` does not handle `ClientMessage.ResumeSession` — it returns a `ProtocolError`
|
||||
* `DomainEventMapper` defaults to `NoopArtifactStore`, but all production call sites (`GlobalStreamHandler`, `SessionStreamHandler`) inject `module.artifactStore` — the `NoopArtifactStore` path is only reachable if a caller omits the argument
|
||||
* `RiskSummaryDto` is hardcoded to `"unknown"` in `mapApprovalRequested` — the approval event does not carry risk information
|
||||
* Session IDs are generated server-side as random UUIDs; client-provided `sessionId` in `StartSessionRequest` is accepted but not used
|
||||
|
||||
== open questions
|
||||
|
||||
* Should the server support session listing by filtering on status or workflow ID?
|
||||
* Should `RiskSummary` data be attached to `ApprovalRequestedEvent` payload, or should the server query it separately?
|
||||
* Is the global stream (`/stream`) scaling strategy adequate for N concurrent clients? The `Channel` buffer size is fixed at 1024.
|
||||
@@ -0,0 +1,115 @@
|
||||
= app-tui
|
||||
|
||||
== purpose
|
||||
|
||||
Terminal UI built on the Tamboulib TUI framework that provides a real-time interactive dashboard for monitoring and controlling Correx sessions.
|
||||
|
||||
== responsibilities
|
||||
|
||||
* Display a live session list with status, workflow name, and background-update badges
|
||||
* Show workflow definitions available on the server for starting new sessions
|
||||
* Stream session lifecycle events (stage, tool, inference) in a scrollable event strip
|
||||
* Display tool diffs when tool executions complete with file modifications
|
||||
* Surface approval requests with tool name, tier, risk summary, and command preview
|
||||
* Accept approval decisions (approve / reject) and steering notes via keyboard input
|
||||
* Support session filtering by workflow name, input history navigation, and keyboard-driven UI modes
|
||||
|
||||
== non-responsibilities
|
||||
|
||||
* Does not contain any domain orchestration or event-sourcing logic
|
||||
* Does not persist any state — all state is in-memory and rebuilt on reconnect
|
||||
* Does not authenticate or encrypt connections
|
||||
* Does not support mouse interaction
|
||||
|
||||
== key types
|
||||
|
||||
=== TuiState
|
||||
* **kind**: data class
|
||||
* **purpose**: Root application state. Contains `connection`, `sessions`, `input`, `provider` sub-states plus display metadata (cursor position, input buffer, history, snapshot phase flags, per-session cursors).
|
||||
|
||||
=== SessionsState
|
||||
* **kind**: data class
|
||||
* **purpose**: Holds the list of `SessionSummary` objects, filter text, session/workflow selection, and background update counter.
|
||||
|
||||
=== DisplayState
|
||||
* **kind**: enum
|
||||
* **purpose**: Tri-state enum (`IDLE`, `IN_SESSION`, `APPROVAL`) that drives which layout the TUI renders.
|
||||
|
||||
=== SessionSummary
|
||||
* **kind**: data class
|
||||
* **purpose**: Projected view of a session for display — ID, status, workflow, stage, tools, recent events, pending approval.
|
||||
|
||||
=== TuiWsClient
|
||||
* **kind**: class
|
||||
* **purpose**: Ktor WebSocket client that maintains a persistent global stream (`/stream`). Exposes cold `Flow<ServerMessage>` and `Flow<ConnectionEvent>` backed by `Channel.UNLIMITED`. Implements exponential backoff reconnection.
|
||||
|
||||
=== Action (sealed interface)
|
||||
* **kind**: sealed interface
|
||||
* **purpose**: Union of all user input and system events (navigation, input editing, approval, server messages, connection events).
|
||||
|
||||
=== KeyEvent (sealed class)
|
||||
* **kind**: sealed class
|
||||
* **purpose**: Normalized key events independent of the TUI backend (TambouiKeyEvent mapping).
|
||||
|
||||
=== RootReducer
|
||||
* **kind**: object
|
||||
* **purpose**: Top-level reducer that coordinates `SnapshotPhaseReducer` + four sub-reducers (`InputReducer`, `SessionsReducer`, `ConnectionReducer`, `ProviderReducer`). Handles cross-reducer state weaving (input history, approval dismissal, auto-enter on session start).
|
||||
|
||||
=== SnapshotPhaseReducer
|
||||
* **kind**: object
|
||||
* **purpose**: Manages the snapshot-to-live transition. Buffers event-bearing messages during snapshot replay; drains buffered events atomically when `SnapshotComplete` arrives; deduplicates live events by `sessionSequence`.
|
||||
|
||||
=== Effect (sealed interface)
|
||||
* **kind**: sealed interface
|
||||
* **purpose**: Side-effect descriptors produced by reducers — either `SendWs(ClientMessage)` or `Quit`.
|
||||
|
||||
=== EffectDispatcher
|
||||
* **kind**: class
|
||||
* **purpose**: Executes effects sequentially. Dispatches WS messages via `TuiWsClient.send()` and `Quit` via a callback that exits the TUI runner.
|
||||
|
||||
== event flow
|
||||
|
||||
*inbound:* `ServerMessage` frames from the global WebSocket stream, decoded by `TuiWsClient` and wrapped in `Action.ServerEventReceived`. Also connection lifecycle events (`Connected`, `Disconnected`, `RetryScheduled`).
|
||||
|
||||
*outbound:* `ClientMessage` frames sent via `Effect.SendWs` — `StartSession`, `CancelSession`, `ApprovalResponse`.
|
||||
|
||||
== integration points
|
||||
|
||||
* `:apps:server` — imports `ServerMessage`, `ClientMessage`, `ApprovalDecision`, `PauseReason`, `WorkflowDto`, `ProviderHealthDto`
|
||||
* `:core:events` — imports `ApprovalRequestId`, `SessionId`
|
||||
* `:core:approvals` — imports `Tier`
|
||||
|
||||
Tamboulib framework: `tamboui-tui`, `tamboui-widgets`, `tamboui-core`, `tamboui-jline3-backend`.
|
||||
|
||||
== invariants
|
||||
|
||||
* `ws.messages` and `ws.connection` are single-consumer cold flows — adding a second collector drops messages from one
|
||||
* Effects are dispatched sequentially (dispatchAll) — order matches `RootReducer` concatenation
|
||||
* `Effect.Quit` is always appended last by `RootReducer.reduce`
|
||||
* Snapshot phase is entered initially and on every `Disconnected`; buffered events are drained atomically at `SnapshotComplete`
|
||||
* Live event deduplication uses per-session `sessionSequence` cursor; gaps are warned and applied anyway
|
||||
* The TUI creates one WS connection (`/stream`) — per-session streams are not used
|
||||
|
||||
== PlantUML diagram
|
||||
|
||||
[plantuml, app-tui, "png"]
|
||||
----
|
||||
include::../../diagrams/app-tui.puml[]
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
== known issues
|
||||
|
||||
* `StageToolManifest` arriving before `SessionStarted` is silently dropped (TODO RF-3) — the session won't exist in the list yet
|
||||
* Chat submission in `IN_SESSION` is blocked (returns empty effects) — router integration is deferred to Epic 14
|
||||
* `InputHistory` limit is hardcoded to 50 entries per session
|
||||
* Event strip shows at most 5 events for the selected session
|
||||
* Session list is capped at 7 visible entries; overflow is shown as a count but not scrollable
|
||||
|
||||
== open questions
|
||||
|
||||
* Should the TUI support per-session WebSocket streams instead of relying solely on the global stream?
|
||||
* Should approval decisions support freeform steering note input that can exceed one line?
|
||||
* How should workflow picker handle very large numbers of available workflows (10+)?
|
||||
@@ -0,0 +1,54 @@
|
||||
= app-worker
|
||||
|
||||
== purpose
|
||||
|
||||
Placeholder application module for a long-running background worker process. Currently contains only a stub `main()` function that prints its identity and exits.
|
||||
|
||||
== responsibilities
|
||||
|
||||
* Declares the module structure and build configuration for a future worker process
|
||||
* Prints a startup banner to stdout
|
||||
|
||||
== non-responsibilities
|
||||
|
||||
* Does not perform any background processing, job queue management, or event consumption
|
||||
* Does not contain any domain logic or infrastructure wiring
|
||||
* Does not connect to any Correx server or event store
|
||||
|
||||
== key types
|
||||
|
||||
=== Main.kt
|
||||
* **kind**: top-level `main()` function
|
||||
* **purpose**: Placeholder entry point. Prints `"correx :: apps/worker"` and exits.
|
||||
|
||||
== event flow
|
||||
|
||||
None. The module has no event interaction.
|
||||
|
||||
== integration points
|
||||
|
||||
* Depends only on `com.github.ajalt.clikt:clikt` in build configuration (no actual usage in source)
|
||||
|
||||
== invariants
|
||||
|
||||
* The module has no runtime state, invariants, or behavioral contracts beyond printing to stdout
|
||||
|
||||
== PlantUML diagram
|
||||
|
||||
[plantuml, app-worker, "png"]
|
||||
----
|
||||
include::../../diagrams/app-worker.puml[]
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
== known issues
|
||||
|
||||
* Not implemented — this is a structural placeholder with no functional code
|
||||
|
||||
== open questions
|
||||
|
||||
* Will the worker process handle queued session execution, inference batching, or tool dispatch at scale?
|
||||
* Should the worker connect to the server via the event store subscription mechanism or a separate message queue?
|
||||
* Does the worker need its own `ServerModule`-style wiring, or will it operate as a stateless consumer?
|
||||
Reference in New Issue
Block a user