feat: FileWrittenEvent + reduce file mutations onto ToolState
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.ToolInvocationId
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* First-class record of a file mutation performed by a tool. Makes file writes
|
||||
* reversible from the log + CAS (invariants #1/#5): [preImageHash]/[postImageHash]
|
||||
* are content-addressed blobs in the ArtifactStore. A newly-created file has
|
||||
* `preExisted = false` and a null [preImageHash]; a delete has a null
|
||||
* [postImageHash]. Undo restores the pre-image blob (or deletes) through the
|
||||
* same atomic write seam.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("FileWritten")
|
||||
data class FileWrittenEvent(
|
||||
val invocationId: ToolInvocationId,
|
||||
val sessionId: SessionId,
|
||||
val path: String,
|
||||
val preImageHash: String? = null,
|
||||
val postImageHash: String? = null,
|
||||
val preExisted: Boolean,
|
||||
val mode: String = "0644",
|
||||
val timestampMs: Long,
|
||||
) : EventPayload
|
||||
@@ -11,6 +11,7 @@ import com.correx.core.events.events.ChatSessionStartedEvent
|
||||
import com.correx.core.events.events.ChatTurnEvent
|
||||
import com.correx.core.events.events.ContextTruncatedEvent
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.FileWrittenEvent
|
||||
import com.correx.core.events.events.L3MemoryRetrievedEvent
|
||||
import com.correx.core.events.events.InferenceCompletedEvent
|
||||
import com.correx.core.events.events.InferenceFailedEvent
|
||||
@@ -51,6 +52,7 @@ val eventModule = SerializersModule {
|
||||
subclass(ToolExecutionFailedEvent::class)
|
||||
subclass(ToolExecutionRejectedEvent::class)
|
||||
subclass(ToolCallAssessedEvent::class)
|
||||
subclass(FileWrittenEvent::class)
|
||||
subclass(SteeringNoteAddedEvent::class)
|
||||
subclass(StageFailedEvent::class)
|
||||
subclass(StageCompletedEvent::class)
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.correx.core.events.serialization
|
||||
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.FileWrittenEvent
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.ToolInvocationId
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class FileWrittenEventSerializationTest {
|
||||
|
||||
private val sample = FileWrittenEvent(
|
||||
invocationId = ToolInvocationId("inv-1"),
|
||||
sessionId = SessionId("sess-1"),
|
||||
path = "src/Main.kt",
|
||||
preImageHash = "aaaa",
|
||||
postImageHash = "bbbb",
|
||||
preExisted = true,
|
||||
mode = "0644",
|
||||
timestampMs = 7L,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `round-trips as polymorphic EventPayload`() {
|
||||
val encoded = eventJson.encodeToString(EventPayload.serializer(), sample)
|
||||
assertTrue(encoded.contains("\"type\":\"FileWritten\""), "SerialName must be present: $encoded")
|
||||
val decoded = eventJson.decodeFromString(EventPayload.serializer(), encoded)
|
||||
assertEquals(sample, decoded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `decodes hand-written FileWritten JSON for a new file`() {
|
||||
val json = """
|
||||
{"type":"FileWritten","invocationId":"inv-9","sessionId":"s","path":"new.txt",
|
||||
"postImageHash":"cccc","preExisted":false,"timestampMs":1}
|
||||
""".trimIndent()
|
||||
val decoded = eventJson.decodeFromString(EventPayload.serializer(), json)
|
||||
assertTrue(decoded is FileWrittenEvent)
|
||||
decoded as FileWrittenEvent
|
||||
assertEquals(false, decoded.preExisted)
|
||||
assertEquals(null, decoded.preImageHash)
|
||||
assertEquals("cccc", decoded.postImageHash)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user