feat(recovery,context): tier-2 intent-holder arbiter + remaining-delta pinning + surfaced signal events

- recovery: two-tier repair ladder — owner then arbiter (recovery stage recast
  as intent-holder, holds initial intent, reconciles cross-file contract disputes)
  with independent INTENT_ROUTE_BUDGET; RecoveryRoutingTest coverage
- context: remaining-delta checklist pinning (RemainingDelta{Pinning,Entry}Test)
- surface write-only events (session name, quality signals) into stats/browse
- parseToolArguments lenient-Json fallback for bare-key drift
- tools: ChildProcess seam
This commit is contained in:
2026-07-11 23:56:52 +04:00
parent 3d5e05c1fb
commit 15248cae8a
55 changed files with 1932 additions and 231 deletions
@@ -54,7 +54,10 @@ class DefaultContextPackBuilder(
// decisionJournal is deliberately NOT here anymore: the journal is OPTIONAL-bucket, capped
// by OptionalCeilings. Pinning is otherwise bucket-driven (ContextBucket.REQUIRED); this set
// only covers entries built outside the orchestrator that never stamp buckets.
private val neverDropSourceTypes = setOf("steeringNote", "eventHistory", "factSheet")
// "remainingDelta" is the shrinking stage-contract checklist (stage-termination design
// 2026-07-11): it must survive every budget/dedup pass, since its entire purpose is to give
// the model a progress signal that outlives the truncation which otherwise wipes its memory.
private val neverDropSourceTypes = setOf("steeringNote", "eventHistory", "factSheet", "remainingDelta")
private companion object {
const val CHARS_PER_TOKEN = 4
@@ -0,0 +1,52 @@
import com.correx.core.context.builder.DefaultContextPackBuilder
import com.correx.core.context.compression.DefaultContextCompressor
import com.correx.core.context.model.ContextEntry
import com.correx.core.context.model.ContextLayer
import com.correx.core.context.model.TokenBudget
import com.correx.core.events.types.ContextEntryId
import com.correx.core.events.types.ContextPackId
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
/**
* The remaining-delta checklist (stage-termination design 2026-07-11) must survive the budget/dedup
* passes — its whole point is to outlive the truncation that gives the model amnesia. It is pinned
* via sourceType "remainingDelta" in [DefaultContextPackBuilder.neverDropSourceTypes].
*/
class RemainingDeltaPinningTest {
private val builder = DefaultContextPackBuilder(DefaultContextCompressor())
private val sessionId = SessionId("s1")
private val stageId = StageId("stage-1")
private val packId = ContextPackId("pack-1")
@Test
fun `remainingDelta entry survives a wildly over-budget build`() = kotlinx.coroutines.runBlocking {
val checklist = ContextEntry(
id = ContextEntryId("delta-1"),
layer = ContextLayer.L1,
content = "## Remaining to finish this stage\n- [ ] Foo.tsx — exports_default_component",
sourceType = "remainingDelta",
sourceId = "remaining-delta",
tokenEstimate = 500,
)
// A large freeform entry that would be dropped to fit the tiny budget.
val filler = ContextEntry(
id = ContextEntryId("filler-1"),
layer = ContextLayer.L2,
content = "lorem ipsum ".repeat(200),
sourceType = "toolResult",
sourceId = "filler-1",
tokenEstimate = 5000,
)
val pack = builder.build(packId, sessionId, stageId, listOf(filler, checklist), TokenBudget(limit = 10))
val retained = pack.layers.values.flatten()
assertTrue(
retained.any { it.sourceType == "remainingDelta" },
"remainingDelta checklist must be retained even when the budget is blown",
)
Unit
}
}