feat(research): dedicated SourceFetched + LowQualityExtraction events (D)
- promote research fetch quality + content_sha256 from ToolExecutionCompletedEvent metadata to first-class SourceFetchedEvent / LowQualityExtractionEvent (registered + serialization test); existing metadata write kept intact (additive) - emitted from SandboxedToolExecutor on research-fetch tool completion, guarded on the url/content_sha256/quality markers; reuses the extractor's LOW_QUALITY marker Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* A research T2 fetch completed and produced content at [contentSha256] (research-workflow-spec §3/§5).
|
||||
*
|
||||
* Promotes the fetch-quality + content-hash that previously lived only inside
|
||||
* [ToolExecutionCompletedEvent] metadata into a first-class event, so "what sources were fetched"
|
||||
* is queryable and renderable directly from the journal. Additive: the metadata write is retained
|
||||
* for existing consumers. [quality] mirrors the extractor's quality marker as recorded in metadata.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("SourceFetched")
|
||||
data class SourceFetchedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val url: String,
|
||||
val contentSha256: String,
|
||||
val quality: String,
|
||||
val byteCount: Int = 0,
|
||||
) : EventPayload
|
||||
|
||||
/**
|
||||
* An extraction was retained but flagged low-quality — below the quality bar (research-workflow-spec §5)
|
||||
* — for operator visibility. Emitted alongside [SourceFetchedEvent] when the fetch cleared rejection
|
||||
* but fell under the minimum-content threshold (JS-rendered SPA, paywall); the content is still kept,
|
||||
* the operator is simply warned that synthesis may want to route around it.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("LowQualityExtraction")
|
||||
data class LowQualityExtractionEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val url: String,
|
||||
val contentSha256: String,
|
||||
val reason: String,
|
||||
) : EventPayload
|
||||
@@ -31,6 +31,7 @@ import com.correx.core.events.events.IdeaDiscardedEvent
|
||||
import com.correx.core.events.events.JournalCompactedEvent
|
||||
import com.correx.core.events.events.FileWrittenEvent
|
||||
import com.correx.core.events.events.L3MemoryRetrievedEvent
|
||||
import com.correx.core.events.events.LowQualityExtractionEvent
|
||||
import com.correx.core.events.events.InferenceCompletedEvent
|
||||
import com.correx.core.events.events.InitialIntentEvent
|
||||
import com.correx.core.events.events.InferenceFailedEvent
|
||||
@@ -46,6 +47,7 @@ import com.correx.core.events.events.RepoMapComputedEvent
|
||||
import com.correx.core.events.events.RetryAttemptedEvent
|
||||
import com.correx.core.events.events.WorkspaceStateObservedEvent
|
||||
import com.correx.core.events.events.RiskAssessedEvent
|
||||
import com.correx.core.events.events.SourceFetchedEvent
|
||||
import com.correx.core.events.events.StageCheckpointFailedEvent
|
||||
import com.correx.core.events.events.StageCheckpointPassedEvent
|
||||
import com.correx.core.events.events.StageCompletedEvent
|
||||
@@ -137,6 +139,8 @@ val eventModule = SerializersModule {
|
||||
subclass(StageCheckpointPassedEvent::class)
|
||||
subclass(StageCheckpointFailedEvent::class)
|
||||
subclass(PossibleContradictionFlaggedEvent::class)
|
||||
subclass(SourceFetchedEvent::class)
|
||||
subclass(LowQualityExtractionEvent::class)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.correx.core.events.serialization
|
||||
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.LowQualityExtractionEvent
|
||||
import com.correx.core.events.events.SourceFetchedEvent
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ResearchSourceEventSerializationTest {
|
||||
|
||||
private val sessionId = SessionId("s")
|
||||
private val stageId = StageId("st")
|
||||
|
||||
@Test
|
||||
fun `SourceFetchedEvent round-trips as polymorphic EventPayload`() {
|
||||
val sample: EventPayload = SourceFetchedEvent(
|
||||
sessionId = sessionId,
|
||||
stageId = stageId,
|
||||
url = "https://example.com/a",
|
||||
contentSha256 = "abc123",
|
||||
quality = "OK",
|
||||
byteCount = 4096,
|
||||
)
|
||||
val encoded = eventJson.encodeToString(EventPayload.serializer(), sample)
|
||||
assertTrue(encoded.contains("\"type\":\"SourceFetched\""), "SerialName must be present: $encoded")
|
||||
val decoded = eventJson.decodeFromString(EventPayload.serializer(), encoded)
|
||||
assertEquals(sample, decoded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LowQualityExtractionEvent round-trips as polymorphic EventPayload`() {
|
||||
val sample: EventPayload = LowQualityExtractionEvent(
|
||||
sessionId = sessionId,
|
||||
stageId = stageId,
|
||||
url = "https://example.com/spa",
|
||||
contentSha256 = "def456",
|
||||
reason = "extracted 12 chars, below minimum 200",
|
||||
)
|
||||
val encoded = eventJson.encodeToString(EventPayload.serializer(), sample)
|
||||
assertTrue(encoded.contains("\"type\":\"LowQualityExtraction\""), "SerialName must be present: $encoded")
|
||||
val decoded = eventJson.decodeFromString(EventPayload.serializer(), encoded)
|
||||
assertEquals(sample, decoded)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user