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.
This commit is contained in:
@@ -12,7 +12,7 @@ import kotlinx.datetime.Clock
|
||||
import java.util.*
|
||||
|
||||
class EventDispatcher(private val eventStore: EventStore) {
|
||||
fun emit(
|
||||
suspend fun emit(
|
||||
payload: EventPayload,
|
||||
sessionId: SessionId,
|
||||
causationId: CausationId? = null,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.ArtifactId
|
||||
import com.correx.core.events.types.InferenceRequestId
|
||||
import com.correx.core.events.types.ProviderId
|
||||
import com.correx.core.events.types.SessionId
|
||||
@@ -14,6 +15,7 @@ data class InferenceStartedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val providerId: ProviderId,
|
||||
val promptArtifactId: ArtifactId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
@@ -24,6 +26,7 @@ data class InferenceCompletedEvent(
|
||||
val providerId: ProviderId,
|
||||
val tokensUsed: TokenUsage,
|
||||
val latencyMs: Long,
|
||||
val responseArtifactId: ArtifactId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -14,12 +14,12 @@ interface EventStore {
|
||||
* - idempotency by eventId
|
||||
* - causal consistency rules (if enforced here, otherwise in kernel)
|
||||
*/
|
||||
fun append(event: NewEvent): StoredEvent
|
||||
suspend fun append(event: NewEvent): StoredEvent
|
||||
|
||||
/**
|
||||
* Append multiple events atomically (same session preferred).
|
||||
*/
|
||||
fun appendAll(events: List<NewEvent>): List<StoredEvent>
|
||||
suspend fun appendAll(events: List<NewEvent>): List<StoredEvent>
|
||||
|
||||
/**
|
||||
* Read events for a session in strict sequence order.
|
||||
@@ -41,4 +41,10 @@ interface EventStore {
|
||||
* Does not replay historical events — use [readFrom] for catch-up before subscribing.
|
||||
*/
|
||||
fun subscribe(sessionId: SessionId): Flow<StoredEvent>
|
||||
|
||||
/**
|
||||
* Iterate every event in the store across all sessions, in monotonic insertion order.
|
||||
* Intended for batch jobs (e.g. CAS liveness scans). Implementations should stream lazily.
|
||||
*/
|
||||
fun allEvents(): Sequence<StoredEvent>
|
||||
}
|
||||
Reference in New Issue
Block a user