Commit Graph

18 Commits

Author SHA1 Message Date
kami 8d7c827ebb fix(server,persistence): stop slow WS client stalling kernel; log dropped emits (#53)
Two event-delivery back-pressure faults from the core audit (#7).

(a) streamGlobal forwarded globalFlow → a per-connection Channel with
buffer.send (SUSPEND). globalFlow is also SUSPEND-overflow, so one wedged
WebSocket client back-pressured globalFlow → append() suspended → the whole
kernel stalled. Now the collector uses trySend; on overflow it closes the
buffer with SlowClientException, tearing down that one connection (client
reconnects and re-syncs via replaySnapshot) instead of blocking append().

(b) Per-session subscriptions (extraBufferCapacity 64) used tryEmit with the
return ignored: a lagging in-process collector (e.g. LiveArtifactRepository)
silently diverged from the durable log. Both SqliteEventStore and
InMemoryEventStore now warnIfDropped() — the event is still persisted; only the
live SharedFlow drops, and we surface it. ponytail: log-only; bounded
rebuild-on-lag if divergence ever bites.

Added slf4j-api to :infrastructure:persistence (already the standard logging
dep across modules) for the warn.

Green: :infrastructure:persistence + :apps:server compile+detekt+test (the one
pre-existing ModelLifecycleWiringTest failure needs a real llama-server, fails
identically on clean checkout).
2026-07-12 17:11:41 +04:00
kami 65d5e9de83 fix(persistence): assign event sequences atomically inside INSERT (#56)
nextGlobalSequence()/nextSessionSequence() computed MAX+1 with two extra
SELECT round-trips, guarded only by an in-process mutex — two processes on the
same correx.db (CLI + server) could read the same MAX and collide on the unique
index. Compute both the global sequence and session_sequence as MAX+1 subqueries
inside the INSERT and RETURNING the assigned values. SQLite serializes writers,
so the subqueries are atomic across processes; the unique index is the backstop.
Also removes the two pre-SELECTs per append.
2026-07-12 13:19:50 +04:00
kami 759828c0f5 fix(persistence): serialize SqliteEventStore reads with append transaction (#52)
Reads (read/readFrom/lastSequence/allEvents/allSessionIds/lastGlobalSequence)
shared the single JDBC Connection with append's transaction unsynchronized.
The coroutine appendMutex only excludes append-vs-append; a read on the caller
thread could hit the connection mid-transaction (setAutoCommit(false)..commit)
and corrupt tx state — SQLite JDBC is not thread-safe. Guard every connection
access with a shared JVM monitor (synchronized(connection)) via withConnection,
so reads and the IO-thread append transaction are mutually exclusive.
2026-07-12 13:17:38 +04:00
kami 8c45650e21 fix(artifacts): rebuild LiveArtifactRepository from event log on cold access 2026-07-12 13:05:52 +04:00
kami d26f20c316 docs(dox): build out the DOX AGENTS.md hierarchy
Root Child DOX Index assembled, plus a per-module AGENTS.md across the tree
(core/*, infrastructure/*, apps/*, testing/*, and docs/examples/frontend/etc),
each following the DOX section shape: Purpose, Ownership, Local Contracts,
Work Guidance, Verification, Child DOX Index.
2026-06-28 20:43:27 +04:00
kami 7936251d6b fix: complete all P2 audit findings — dead code, NPE guard, hygiene
P2-1: Guard SessionId NPE with safe-call in SessionsReducer
P2-2: Remove 16 dead event classes + reducer branches; replace with live orchestration events in tests
P2-3: Standardize ChatInput divergences — guard CancelSession, single-arg ProtocolError
P2-4: Remove dead TuiToolRecord.diff and ToolDisplayStatus.REQUESTED
P2-5: Remove dead streamLive from SessionEventBridge
P2-6: Extract SessionsReducerContext data class for 6+ param method
P2-7: Replace StageId("none") sentinel with TypeId.NONE
P2-9: Add TypeId.random() factory on all type-alias IDs
P2-10: Add KDoc on schemaVersion documenting reserved-for-migration
P2-8: Verified RouterReducer string-template already correct (TypeId value class toString)
2026-05-29 01:01:45 +04:00
kami cdee5f2245 fix: harden event store, serialization, and grant engine; wire router chat
Event log integrity (sole source of truth):
- SqliteEventStore: serialize append/appendAll under a Mutex, enable WAL +
  busy_timeout + synchronous=NORMAL, roll back on any Throwable. Replaces the
  app-computed `SELECT MAX(seq)+1` read-modify-write that dropped events under
  concurrent appends (race was invisible to CI: only the in-memory store and
  single-threaded :memory: tests were ever exercised).
- Serialization: encodeDefaults + ignoreUnknownKeys, explicit @SerialName on all
  46 EventPayload subclasses and event-referenced enum constants, @Serializable on
  RiskLevel/RiskAction. Guards the append-only log against silent corruption from
  future class renames, field removals, or default-value changes.

Approval/grant security (Invariant: approvals cannot widen authority):
- Tier authorization is now a ceiling (request.tier.level <= max granted) instead
  of set membership; SESSION grants bind to a specific toolName instead of matching
  everything; handleCreateGrant rejects empty/over-tier/scopeless grants.

Router chat wiring (Phase 0-2): ChatInput round-trip, StartChatSession from IDLE,
router-panel layout, workflows behind Ctrl+W.

Tests: SqliteEventStore concurrency, serialization round-trip + discriminator
stability + unknown-key tolerance, adversarial grant scope/tier; updated existing
approval and reducer tests for the new grant semantics and StartChatSession effect.
2026-05-28 22:39:15 +04:00
kami ac05ad8733 chore(clean up): remove unused imports, unnecessary object impls, concurrency/coroutine issues, trailing commas, etc. 2026-05-24 22:56:19 +04:00
kami 70420083ac feat(server-02): promote EventStore to global-sequence store with subscribeAll
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.
2026-05-24 22:23:43 +04:00
kami fc7b879891 feature(event-sourcing): baseline for the architecture review fixes.
- 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.
2026-05-24 18:57:56 +04:00
kami f827685ed0 chore(damn): detekt, build, tests, formatting
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.
2026-05-22 00:10:05 +04:00
kami 2c459da009 feat(router): implement Epic 14 — core:router module
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
2026-05-21 15:06:20 +04:00
kami 71a73a4fa2 chore(audit): tool path resolution, config file, dead code removal, static analysis cleanup
Tool fixes:
- FileWriteTool, FileEditTool: resolve relative paths against workingDir instead of JVM cwd
- FileReadTool: remove allowedPaths restriction — reads are unrestricted
- ShellTool: add workingDir for ProcessBuilder, remove environment().clear(), fail-open when allowedExecutables empty

Config:
- Add [tools] section to config.toml: sandbox_root, working_dir, shell_allowed_executables, default_system_prompt_path
- Three-level resolution: env var > config file > hardcoded default
- OrchestrationConfig sandboxRoot and defaultSystemPromptPath now sourced from CorrexConfig
- Single defaultOrchestrationConfig in ServerModule, shared by SessionRoutes and GlobalStreamHandler

Dead code:
- Delete EventTypes.kt — orphaned string constants duplicating serialization annotations

Static analysis:
- Remove 10 unused imports across 9 files
- Remove redundant return in ReplayOrchestrator
- Remove redundant suspend from ApprovalCoordinator.handleResponse
- Fix unreachable InputMode.None branch in InputBar
- Enable full detekt rule sets in detekt.yml
2026-05-19 12:48:47 +04:00
kami 219e2c762e feat(cas): content-addressed artifact store (steps 1–8)
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.
2026-05-18 12:22:38 +04:00
kami 7d46f46f01 fix(server): event capturing and necessary logic for smoke test. 2026-05-17 03:21:42 +04:00
kami f77efce10b debt(pre-router): resolved most of the technical debt that was noted while finishing epic-12 and epic-13. 2026-05-17 03:21:26 +04:00
kami ce723afc8b refactor(imports): optimize imports 2026-05-17 03:19:57 +04:00
kami c77277af0b epic-12: after epic audit and init commit 2026-05-17 03:19:39 +04:00