feat(freestyle): soft-lock steering preemption (Slice 5)

- DecisionKind gains PREEMPT; DecisionJournalState tracks planLocked.
- DefaultDecisionJournalReducer: ExecutionPlanLockedEvent flips planLocked
  (no record); post-lock steering notes recorded as PREEMPT with a
  'PRIORITY DIRECTIVE' summary, pre-lock stay STEERING.
- SessionOrchestrator.buildSteeringNoteEntries: post-lock notes surfaced as
  pinned L0/SYSTEM 'PRIORITY DIRECTIVE' entries ahead of the plan; pre-lock
  keep L2. Graph re-routing intentionally deferred (priority surfacing only).
- Tests: PreemptSteeringJournalTest, SteeringPreemptionTest.
This commit is contained in:
2026-06-08 02:58:21 +04:00
parent 37ac767d1f
commit 4d1b4ffbb6
6 changed files with 314 additions and 5 deletions
@@ -0,0 +1,49 @@
import com.correx.core.events.events.ExecutionPlanLockedEvent
import com.correx.core.events.events.SteeringNoteAddedEvent
import com.correx.core.events.types.ArtifactId
import com.correx.core.events.types.SessionId
import com.correx.core.journal.DecisionJournalProjector
import com.correx.core.journal.DefaultDecisionJournalReducer
import com.correx.core.journal.model.DecisionKind
import com.correx.testing.fixtures.EventFixtures.stored
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
class PreemptSteeringJournalTest {
private val projector = DecisionJournalProjector(DefaultDecisionJournalReducer())
@Test
fun `steering before plan lock is STEERING, steering after is PREEMPT`() {
var s = projector.initial()
s = projector.apply(
s,
stored(payload = SteeringNoteAddedEvent(SessionId("x"), "use jwt"), sequence = 1L),
)
s = projector.apply(
s,
stored(
payload = ExecutionPlanLockedEvent(
sessionId = SessionId("x"),
planArtifactId = ArtifactId("plan-1"),
workflowId = "wf1",
stageIds = listOf("stage1"),
startStageId = "stage1",
),
sequence = 2L,
),
)
s = projector.apply(
s,
stored(payload = SteeringNoteAddedEvent(SessionId("x"), "skip tests"), sequence = 3L),
)
assertEquals(2, s.records.size)
assertEquals(DecisionKind.STEERING, s.records[0].kind)
assertEquals(DecisionKind.PREEMPT, s.records[1].kind)
assertTrue(
s.records[1].summary.startsWith("PRIORITY DIRECTIVE"),
"Expected summary to start with 'PRIORITY DIRECTIVE' but was: ${s.records[1].summary}",
)
}
}