Add three new fields to support the snapshot/live streaming protocol phase:
- snapshotPhase: Boolean (true during initial snapshot)
- pendingEvents: List<ServerMessage> (FIFO buffer during snapshot)
- cursors: Map<String, Long> (per-session high-water marks for deduplication)
All fields include KDoc documenting their invariants and purpose. Additive
change only; all existing tests continue to pass.
Establishes subscribeAll() before replaySnapshot() so no event can slip between the
projection read and live forwarding. Removes liveStreamJobs map and per-session forwarder
spawn from handleStartSession; all live forwarding flows through the single global
subscription. Closes findings #1 (race) and #5 (truth sources).
Tests cover happy path, race (x100), empty sessions, and shutdown/leak cases.
SessionOrchestratorIntegrationTest: wire approvalRepository into
OrchestratorRepositories constructor which gained the field in the kernel
refactor. TambouiKeyMapperTest: correct ctrl-h to alt-h — the mapper has
always placed ToggleApprovalOverlay under the Alt branch, not Ctrl.
SessionOrchestrator.emit: replace substring(0,7) with take(7) to avoid
StringIndexOutOfBoundsException when session IDs are shorter than 7 chars.
Capture lastGlobalSequence() before any projection read to anchor the
buffer-drain window; capture lastSequence(sessionId) before each session's
projection read. SessionSnapshot.lastSequence now carries the global cursor
shared across all sessions in the batch. pendingApprovals populated from
filtered approval state and sorted by (timestamp, requestId) for deterministic
ordering. SnapshotComplete emitted unconditionally including the zero-sessions
case. Four new tests: zero-sessions, SnapshotComplete-last ordering, global
cursor isolation, and per-session cursor value.
Adds a file-private SLF4J logger and replaces the silent else->null branch
with a debug log line that names the payload type, sessionId, and sequence.
Test extended with a log-capture case that verifies exactly one DEBUG event
is emitted containing the payload class name for unrecognised event types.
StoredEvent gains sessionSequence: Long (per-session monotonic); existing
sequence field becomes the global monotonic cursor across the entire store.
SqliteEventStore allocates both counters inside the same BEGIN IMMEDIATE
transaction and emits to a global MutableSharedFlow after commit.
InMemoryEventStore mirrors this with an AtomicLong global counter and a
suspend-on-overflow SharedFlow. EventStore interface gains subscribeAll()
and lastGlobalSequence(). Contract tests updated and extended to cover
global-sequence and cross-session subscribeAll invariants.
Every event-derived ServerMessage gains sequence (global) and sessionSequence
(per-session) cursor fields. SessionSnapshot adds lastSequence and
lastSessionSequence at projection-read time. SnapshotComplete data object
terminates the snapshot phase and is registered in the polymorphic module.
replaySnapshot() now emits SnapshotComplete after all SessionSnapshot messages.
SessionStarted audit deferred with TODO comment. DomainEventMapper propagates
both cursors to all mapped messages including InferenceCompleted.
- fixed the tool overlay in tui.
- fixed tool calling - added schemas.
- getting all sessionIds via event store.
- instead of sending all events on the replay - there's a new way for replaying.
- session snapshot is now a thing getting sent for previously created sessions.
- added logging to important parts.
- revert workflowId from WorkflowCompletedEvent.
fixed detekt issues where possible.
fixed disttar failing build because tools is added twice in the server module.
added workflowId where required.
fixed some tests not being recognized because of runBlocking without explicit return type.
formatting + imports.
Implements the full conversational router facade: RouterState, RouterReducer,
RouterProjector, RouterRepository, RouterContextBuilder, RouterFacade, protocol
types, WebSocket wiring, infrastructure factory, and deterministic test suite.
Also fixes spec divergences found in post-implementation review:
- Add SteeringNote domain object to core:context (epic prerequisite)
- Rename RouterFacade.handleChat → onUserInput per spec interface contract
- Add in-memory ConcurrentHashMap conversation history to DefaultRouterFacade
- Make RouterRepository.getRouterState suspend
- Rename RouterConfig.keepLast → conversationKeepLast, fix defaults (6, 4096)
- Refactor InfrastructureModule.createRouterFacade to self-assemble internally
- Fix FileReadTool: allowedPaths was dead constructor param (@SuppressUnusedParameter);
now stored as private val and enforced in validateRequest
- Disable koverVerify on modules tested via testing/ submodules or with
hardware/integration dependencies (24 modules); build gate now passes clean
- Layout: status bar (borderless), horizontal top row (sessions 30% / active 30% / tools 40%), router fill, input bar fixed
- Components: rewrote all 5 + added ToolsPanel; all accept TuiState directly
- InputBar: 4 modes (ROUTER/NAVIGATE/FILTER/STEER) with context-sensitive keybind hints
- RouterPanel: inline approval/event overlays as last-child text panels
- StatusBar: borderless row with model, providerType, session count, approval badge
- SessionList: manual column/row rendering, no list widget, overflow truncation
- tamboui bumped to 0.3.0
- TambouiKeyMapper: ctrl+key required for all keybinds, bare chars always
go to input buffer.
- KeyResolver: Quit is ROUTER-only (not global); NavUp/NavDown handled in
ROUTER as well as NAVIGATE; Filter in ROUTER maps to OpenFilter; CharInput
in ROUTER returns null (semantic chars handled upstream by mapper).
Sync all TUI reducer and input tests with the state model refactor:
InputMode.None/WorkflowId/SteeringNote/Filter → ROUTER/NAVIGATE/STEER/FILTER;
InputState(mode, text) → TuiState(inputMode, inputBuffer); fix SessionStarted
effect assertion to expect ConnectSession.
Lifts inputMode/inputBuffer out of InputState into top-level TuiState,
renames InputMode variants to ROUTER/NAVIGATE/FILTER/STEER, adds
TuiToolRecord and TuiEventEntry for per-session tool/event history,
expands SessionsReducer to track tool lifecycle and recent events, and
adds ToggleApprovalOverlay/ToggleEventOverlay/EnterSteer actions.
Deletes 21 Module.kt scaffolding objects that were never wired into any DI
registry. Removes unused imports across 8 production and 3 test files.
Restores StageExecutor, CyclePolicyResolver, PolicyValidation, and
ReplayContractTest — these are deferred features, not dead code.
Removes 6 files audited as fully unused: StageExecutor interface (shadowed
by kernel), CyclePolicyResolver and PolicyValidation (deferred cycle policy),
ApprovalRoutes (never registered), TuiNavigation (superseded by reducer),
and ReplayContractTest abstract class (no concrete subclasses).
ArtifactRelationshipType and CyclePolicy were audited but retained —
both are actively referenced by production code.
- SessionEventBridge: replaySnapshot() sends all historical events on connect;
streamLive(sessionId) subscribes to live event flow per session
- DomainEventMapper: maps 14 domain event types to ServerMessage; reads
InferenceCompleted response text from ArtifactStore with empty-string fallback
- GlobalStreamHandler: wires bridge on connect for replay, launches per-session
live-stream job on StartSession, cancels all jobs on disconnect; removes
ProtocolError("ping") heartbeat hack
- SessionStreamHandler: fixes replay to use DomainEventMapper instead of
emitting SessionStarted for every event; injects real artifactStore;
removes ProtocolError("ping") heartbeat; adds structured logging throughout
- Main: startup log block showing port, model config, sandbox, stores, tools,
providers
- SessionsReducer: StageCompleted/StageFailed now clears currentStage; adds
5 missing tests (InferenceCompleted, StageStarted, ToolCompleted, SessionPaused)
- RouterPanel: new TUI widget rendering active session's lastResponseText
- TuiWsClient: remove println that was polluting the TUI on WS connect failure
New core:artifacts-store interface + infrastructure/artifacts-cas
implementation: segment files + SQLite index, Blake3 hashing,
group-commit fsync via flushBefore, recovery tail-scan, manual
compactor, and oldest-first disk-cap evictor.
Inference events now carry promptArtifactId / responseArtifactId;
orchestrators put bytes before emitting. SqliteEventStore wraps
its txn in artifactStore.flushBefore so segment data is fsynced
before the event commit, making the crash window non-corrupting
(TailScanner re-indexes orphan tail records on reopen).
Compactor and evictor are mutually exclusive via maintenanceMutex.
Step 9 (cloud sync) deferred to a later epic.
See docs/reviews/2026-05-18-cas-steps-1-8-review.md for the final
review.
Add log4j2/SLF4J deps and log4j2.xml, install Ktor CallLogging, replace
manual System.err.println sites with leveled loggers, and wrap the
EventStore with a LoggingEventStore decorator so every append/appendAll
is logged in one place.
Replaces the Mosaic 0.18 string-frame renderer with tamboui 0.2.1-SNAPSHOT
to unlock a cell buffer, widget catalog, and real layout engine. Domain
core (state/reducer/ws/input) is unchanged; only the renderer and the key
mapper are rewritten. Fixes a latent Mosaic-mapper bug where q/n/c/a/r/s//
were swallowed inside input modes by making the new mapper mode-aware.
Tasks 4.2–4.3 of docs/plans/2026-05-17-tui-refactor.md:
- SessionList filters by workflowId (case-insensitive) using
state.sessions.filter, populated by SubmitInput while in
InputMode.Filter and cleared on CancelInput.
- TuiWsClient tracks reconnect attempt count and emits
ConnectionEvent.RetryScheduled(attempt, nextRetryAtMs) before each
backoff delay. Attempt resets on successful connect.
- StatusBar renders "reconnecting (attempt N, retry in Xs)" with
recompose-driven countdown.
Also captures a renderer-evaluation note in the progress doc: Mosaic's
ceiling vs Textual, candidate JVM alternatives (tamboui, Kotter,
Lanterna, Mordant), and confirmation that the reducer/Effect core is
renderer-agnostic if a swap is ever pursued.
Pending: 4.1 (scroll log, deferred), 5.1 (coverage gate), 5.2 (cleanup).