feat(concept): heuristic concept compiler (ACR fold-in)
Promotes recurring validated failure->fix patterns into L3 as retrieval-on-demand concepts. Deterministic core: ConceptCompilerProjection clusters RetryAttempted->StageCompleted pairs by fingerprint (gate-agnostic), promotes at N=3 cross-session validated fixes, never-contradicted. ConceptPromotedEvent is the sole authoritative write (idempotent under replay); ConceptCompilerService appends it + best-effort injects to L3 (non-authoritative, inv #6). Wired live in ServerModule.start() on StageCompleted. Design: docs/plans/2026-07-12-acr-concept-compiler.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhFXmKe4WisSSPf9LrmmTg
This commit is contained in:
@@ -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
|
||||
|
||||
/**
|
||||
* A validated failure→fix pattern the heuristic concept compiler has seen resolved reliably enough to
|
||||
* promote (design 2026-07-12-acr-concept-compiler.md). The only "write" the compiler makes: a
|
||||
* deterministic rule firing — a [fingerprint] whose validated-fix count crossed the promotion
|
||||
* threshold, never contradicted — so it needs no assessor gate (invariant #3/#7 hold trivially:
|
||||
* nothing model-proposed reaches state here).
|
||||
*
|
||||
* Read-only over the log otherwise: the promotion is derivable from the RetryAttempted→StageCompleted
|
||||
* stream, and this event makes the promotion idempotent under replay (the compiler folds already-emitted
|
||||
* fingerprints into its promoted set and never re-promotes). Best-effort injected into L3 as a
|
||||
* retrieval-on-demand concept; the L3 write is non-authoritative (invariant #6) — this event is truth.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("ConceptPromoted")
|
||||
data class ConceptPromotedEvent(
|
||||
// A dedicated system session (ConceptCompilerService.SYSTEM_SESSION) — concepts are cross-session,
|
||||
// so they live on their own stream rather than polluting any user session's replay.
|
||||
val sessionId: SessionId,
|
||||
// Gate-agnostic fingerprint of the recurring failure (see FailureFingerprint).
|
||||
val fingerprint: String,
|
||||
// The gate the failure recurred on ("contract" | "review" | "plan-compile" | "lint" | "static" | …).
|
||||
val gate: String,
|
||||
// Templated, retrieval-friendly concept text injected into L3.
|
||||
val conceptText: String,
|
||||
// Count of validated fixes observed at promotion time (≥ threshold).
|
||||
val occurrences: Int,
|
||||
) : EventPayload
|
||||
@@ -14,6 +14,7 @@ import com.correx.core.events.events.ArtifactValidatedEvent
|
||||
import com.correx.core.events.events.ArtifactValidatingEvent
|
||||
import com.correx.core.events.events.BriefEchoMismatchEvent
|
||||
import com.correx.core.events.events.BriefGroundingCheckedEvent
|
||||
import com.correx.core.events.events.ConceptPromotedEvent
|
||||
import com.correx.core.events.events.StaticAnalysisCompletedEvent
|
||||
import com.correx.core.events.events.ContractGateEvaluatedEvent
|
||||
import com.correx.core.events.events.PlanLintCompletedEvent
|
||||
@@ -128,6 +129,7 @@ val eventModule = SerializersModule {
|
||||
subclass(SessionNamedEvent::class)
|
||||
subclass(StageFailedEvent::class)
|
||||
subclass(StageCompletedEvent::class)
|
||||
subclass(ConceptPromotedEvent::class)
|
||||
subclass(TransitionExecutedEvent::class)
|
||||
subclass(ApprovalRequestedEvent::class)
|
||||
subclass(ApprovalDecisionResolvedEvent::class)
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.correx.core.events.serialization
|
||||
|
||||
import com.correx.core.events.events.ConceptPromotedEvent
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.types.SessionId
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ConceptPromotedEventSerializationTest {
|
||||
|
||||
private val sample = ConceptPromotedEvent(
|
||||
sessionId = SessionId("__concept_compiler__"),
|
||||
fingerprint = "1a2b3c",
|
||||
gate = "lint",
|
||||
conceptText = "Recurring lint failure (validated-fixed 3x across sessions): detekt: MagicNumber.",
|
||||
occurrences = 3,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `round-trips as polymorphic EventPayload`() {
|
||||
val encoded = eventJson.encodeToString(EventPayload.serializer(), sample)
|
||||
assertTrue(encoded.contains("\"type\":\"ConceptPromoted\""), "SerialName must be present: $encoded")
|
||||
val decoded = eventJson.decodeFromString(EventPayload.serializer(), encoded)
|
||||
assertEquals(sample, decoded)
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package com.correx.core.kernel.concept
|
||||
|
||||
import com.correx.core.events.events.ConceptPromotedEvent
|
||||
import com.correx.core.events.events.RetryAttemptedEvent
|
||||
import com.correx.core.events.events.StageCompletedEvent
|
||||
import com.correx.core.events.events.StageFailedEvent
|
||||
import com.correx.core.events.events.StoredEvent
|
||||
import com.correx.core.events.events.WorkflowFailedEvent
|
||||
import com.correx.core.sessions.projections.Projection
|
||||
|
||||
/** Default validated-fix count a fingerprint must reach before promotion (design §"Promotion rule"). */
|
||||
const val DEFAULT_PROMOTION_THRESHOLD = 3
|
||||
|
||||
/** A recurring failure signature and its resolution tally, keyed by [RetryAttemptedEvent.fingerprint]. */
|
||||
data class ConceptCluster(
|
||||
val fingerprint: String,
|
||||
val gate: String,
|
||||
// First line of the recurring failure reason — the retrieval-friendly signature.
|
||||
val signature: String,
|
||||
// Count of times a stage retrying this fingerprint went on to complete = validated failure→fix.
|
||||
val validatedFixes: Int = 0,
|
||||
// Any StageFailed / WorkflowFailed observed while this fingerprint was open: the fix did NOT hold.
|
||||
val contradicted: Boolean = false,
|
||||
)
|
||||
|
||||
/** A fingerprint currently being fought by a stage — resolved by the next StageCompleted/Failed. */
|
||||
data class OpenFailure(val fingerprint: String, val gate: String, val signature: String)
|
||||
|
||||
data class ConceptCompilerState(
|
||||
val clusters: Map<String, ConceptCluster> = emptyMap(),
|
||||
// fingerprints already emitted as ConceptPromotedEvent — folded back so replay never re-promotes.
|
||||
val promoted: Set<String> = emptySet(),
|
||||
// Per-stage open failure the stage is retrying; keyed by "sessionId/stageId" so concurrent sessions
|
||||
// in one cross-session fold don't collide.
|
||||
val open: Map<String, OpenFailure> = emptyMap(),
|
||||
) {
|
||||
/**
|
||||
* Clusters that have earned promotion but haven't been emitted yet: validated ≥ [threshold], never
|
||||
* contradicted, not already promoted. Pure — the caller (a service) appends the ConceptPromotedEvent.
|
||||
*/
|
||||
fun promotable(threshold: Int = DEFAULT_PROMOTION_THRESHOLD): List<ConceptCluster> =
|
||||
clusters.values.filter {
|
||||
it.fingerprint !in promoted && !it.contradicted && it.validatedFixes >= threshold
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read-only projection over the event log that clusters validated failure→fix pairs by
|
||||
* [RetryAttemptedEvent.fingerprint] (gate-agnostic — contract/review/plan-compile/lint/static all
|
||||
* flow through the same retry event) and tracks which fingerprints are promotable. Fed the full
|
||||
* cross-session stream by its driver; the fold is deterministic and replayable (invariant #8).
|
||||
*
|
||||
* Signal: a stage emits RetryAttemptedEvent(fingerprint=fp) then StageCompleted ⇒ fp was validated-fixed.
|
||||
* A StageFailed/WorkflowFailed while fp is still open ⇒ the fix didn't hold (contradiction).
|
||||
*/
|
||||
class ConceptCompilerProjection : Projection<ConceptCompilerState> {
|
||||
|
||||
override fun initial() = ConceptCompilerState()
|
||||
|
||||
override fun apply(state: ConceptCompilerState, event: StoredEvent): ConceptCompilerState =
|
||||
when (val p = event.payload) {
|
||||
is RetryAttemptedEvent -> {
|
||||
val key = "${p.sessionId.value}/${p.stageId.value}"
|
||||
val sig = p.failureReason.lineSequence().firstOrNull()?.take(SIGNATURE_MAX)?.trim().orEmpty()
|
||||
state.copy(open = state.open + (key to OpenFailure(p.fingerprint, p.gate, sig)))
|
||||
}
|
||||
is StageCompletedEvent -> resolve(state, "${p.sessionId.value}/${p.stageId.value}", fixed = true)
|
||||
is StageFailedEvent -> resolve(state, "${p.sessionId.value}/${p.stageId.value}", fixed = false)
|
||||
is WorkflowFailedEvent -> failAllInSession(state, p.sessionId.value)
|
||||
is ConceptPromotedEvent -> state.copy(promoted = state.promoted + p.fingerprint)
|
||||
else -> state
|
||||
}
|
||||
|
||||
private fun resolve(state: ConceptCompilerState, key: String, fixed: Boolean): ConceptCompilerState {
|
||||
val failure = state.open[key] ?: return state
|
||||
val existing = state.clusters[failure.fingerprint]
|
||||
?: ConceptCluster(failure.fingerprint, failure.gate, failure.signature)
|
||||
val updated = if (fixed) {
|
||||
existing.copy(validatedFixes = existing.validatedFixes + 1)
|
||||
} else {
|
||||
existing.copy(contradicted = true)
|
||||
}
|
||||
return state.copy(
|
||||
clusters = state.clusters + (failure.fingerprint to updated),
|
||||
open = state.open - key,
|
||||
)
|
||||
}
|
||||
|
||||
// A workflow that failed terminally contradicts every fingerprint still open in that session.
|
||||
private fun failAllInSession(state: ConceptCompilerState, sessionId: String): ConceptCompilerState {
|
||||
val prefix = "$sessionId/"
|
||||
var acc = state
|
||||
state.open.keys.filter { it.startsWith(prefix) }.forEach { acc = resolve(acc, it, fixed = false) }
|
||||
return acc
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SIGNATURE_MAX = 200
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package com.correx.core.kernel.concept
|
||||
|
||||
import com.correx.core.events.events.ConceptPromotedEvent
|
||||
import com.correx.core.events.events.EventMetadata
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.RetryAttemptedEvent
|
||||
import com.correx.core.events.events.StageCompletedEvent
|
||||
import com.correx.core.events.events.StageFailedEvent
|
||||
import com.correx.core.events.events.StoredEvent
|
||||
import com.correx.core.events.events.WorkflowFailedEvent
|
||||
import com.correx.core.events.types.EventId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import com.correx.core.events.types.TransitionId
|
||||
import kotlinx.datetime.Instant
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ConceptCompilerProjectionTest {
|
||||
|
||||
private val projection = ConceptCompilerProjection()
|
||||
private var seq = 0L
|
||||
|
||||
private fun stored(payload: EventPayload, session: String = "s1") = StoredEvent(
|
||||
metadata = EventMetadata(
|
||||
eventId = EventId("e${seq++}"),
|
||||
sessionId = SessionId(session),
|
||||
timestamp = Instant.parse("2026-01-01T00:00:00Z"),
|
||||
schemaVersion = 1,
|
||||
causationId = null,
|
||||
correlationId = null,
|
||||
),
|
||||
sequence = seq,
|
||||
sessionSequence = seq,
|
||||
payload = payload,
|
||||
)
|
||||
|
||||
private fun retry(fp: String, stage: String = "impl", session: String = "s1", reason: String = "boom\ndetail") =
|
||||
stored(
|
||||
RetryAttemptedEvent(
|
||||
sessionId = SessionId(session),
|
||||
stageId = StageId(stage),
|
||||
attemptNumber = 1,
|
||||
maxAttempts = 3,
|
||||
failureReason = reason,
|
||||
gate = "lint",
|
||||
fingerprint = fp,
|
||||
),
|
||||
session,
|
||||
)
|
||||
|
||||
private fun completed(stage: String = "impl", session: String = "s1") =
|
||||
stored(StageCompletedEvent(SessionId(session), StageId(stage), TransitionId("t")), session)
|
||||
|
||||
private fun failed(stage: String = "impl", session: String = "s1") =
|
||||
stored(StageFailedEvent(SessionId(session), StageId(stage), TransitionId("t"), "x"), session)
|
||||
|
||||
private fun fold(events: List<StoredEvent>) =
|
||||
events.fold(projection.initial(), projection::apply)
|
||||
|
||||
@Test
|
||||
fun `retry then StageCompleted counts one validated fix`() {
|
||||
val state = fold(listOf(retry("fp1"), completed()))
|
||||
assertEquals(1, state.clusters["fp1"]?.validatedFixes)
|
||||
assertTrue(state.promotable().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `three cross-session validated fixes cross the threshold`() {
|
||||
val state = fold(
|
||||
listOf(
|
||||
retry("fp1", session = "sa"), completed(session = "sa"),
|
||||
retry("fp1", session = "sb"), completed(session = "sb"),
|
||||
retry("fp1", session = "sc"), completed(session = "sc"),
|
||||
),
|
||||
)
|
||||
assertEquals(3, state.clusters["fp1"]?.validatedFixes)
|
||||
assertEquals(listOf("fp1"), state.promotable().map { it.fingerprint })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `StageFailed while fingerprint open contradicts the cluster`() {
|
||||
val state = fold(
|
||||
listOf(
|
||||
retry("fp1", session = "sa"), completed(session = "sa"),
|
||||
retry("fp1", session = "sb"), completed(session = "sb"),
|
||||
retry("fp1", session = "sc"), failed(session = "sc"),
|
||||
),
|
||||
)
|
||||
assertTrue(state.clusters["fp1"]!!.contradicted)
|
||||
assertTrue(state.promotable().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WorkflowFailed contradicts every open fingerprint in that session`() {
|
||||
val state = fold(
|
||||
listOf(
|
||||
retry("fp1", stage = "a", session = "sa"),
|
||||
retry("fp2", stage = "b", session = "sa"),
|
||||
stored(WorkflowFailedEvent(SessionId("sa"), StageId("a"), "dead", true), "sa"),
|
||||
),
|
||||
)
|
||||
assertTrue(state.clusters["fp1"]!!.contradicted)
|
||||
assertTrue(state.clusters["fp2"]!!.contradicted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `promotion is idempotent - a promoted fingerprint is not promotable again`() {
|
||||
val base = listOf(
|
||||
retry("fp1", session = "sa"), completed(session = "sa"),
|
||||
retry("fp1", session = "sb"), completed(session = "sb"),
|
||||
retry("fp1", session = "sc"), completed(session = "sc"),
|
||||
)
|
||||
val promoted = base + stored(ConceptPromotedEvent(SessionId("sc"), "fp1", "lint", "concept", 3))
|
||||
assertTrue(fold(promoted).promotable().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `signature is the first line of the failure reason`() {
|
||||
val state = fold(listOf(retry("fp1", reason = "detekt: MagicNumber\ntrace..."), completed()))
|
||||
assertEquals("detekt: MagicNumber", state.clusters["fp1"]?.signature)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user