From 3ac0a140ec6002f0f7e914647d07be4f4583c0a0 Mon Sep 17 00:00:00 2001 From: kami Date: Sun, 14 Jun 2026 14:21:40 +0400 Subject: [PATCH] feat(events): idea capture/discard events IdeaCapturedEvent(ideaId, sessionId, text, timestampMs) + IdeaDiscardedEvent (ideaId, sessionId) tombstone, registered in eventModule. The cross-session idea board is rebuilt from these via eventStore.allEvents(); discard hides without deleting (invariant #1). --- .../correx/core/events/events/IdeaEvents.kt | 33 +++++++++++++++++++ .../events/serialization/Serialization.kt | 4 +++ 2 files changed, 37 insertions(+) create mode 100644 core/events/src/main/kotlin/com/correx/core/events/events/IdeaEvents.kt diff --git a/core/events/src/main/kotlin/com/correx/core/events/events/IdeaEvents.kt b/core/events/src/main/kotlin/com/correx/core/events/events/IdeaEvents.kt new file mode 100644 index 00000000..6f7629c9 --- /dev/null +++ b/core/events/src/main/kotlin/com/correx/core/events/events/IdeaEvents.kt @@ -0,0 +1,33 @@ +package com.correx.core.events.events + +import com.correx.core.events.types.SessionId +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * An idea the operator surfaced while rubber-ducking with the router, captured to a cross-session + * scratchpad. Recorded as a fact (invariant #1) so the board is rebuilt from the log via + * `eventStore.allEvents()`. Non-authoritative: an idea informs the router's context but never drives + * the kernel — it is a note, not a decision. + */ +@Serializable +@SerialName("IdeaCaptured") +data class IdeaCapturedEvent( + val ideaId: String, + val sessionId: SessionId, + val text: String, + val timestampMs: Long, +) : EventPayload + +/** + * The operator removed an idea from the board. The event log is append-only (invariant #1), so this + * is a tombstone — the original [IdeaCapturedEvent] stays in the log (and replays), but the idea-board + * projection filters out any [ideaId] that has a discard. [sessionId] is wherever the discard was + * issued (often a different session than the capture). + */ +@Serializable +@SerialName("IdeaDiscarded") +data class IdeaDiscardedEvent( + val ideaId: String, + val sessionId: SessionId, +) : EventPayload diff --git a/core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt b/core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt index 4048567c..cbf6cf76 100644 --- a/core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt +++ b/core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt @@ -23,6 +23,8 @@ import com.correx.core.events.events.ExecutionPlanLockedEvent import com.correx.core.events.events.ExecutionPlanRejectedEvent import com.correx.core.events.events.HealthDegradedEvent import com.correx.core.events.events.HealthRestoredEvent +import com.correx.core.events.events.IdeaCapturedEvent +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 @@ -123,6 +125,8 @@ val eventModule = SerializersModule { subclass(ClarificationRequestedEvent::class) subclass(ClarificationAnsweredEvent::class) subclass(WorkflowProposedEvent::class) + subclass(IdeaCapturedEvent::class) + subclass(IdeaDiscardedEvent::class) } }