feat(context): reasoning-thread continuity, L3 doc filtering, and gate hardening

Threads reasoning_content across inference calls and journal renders, filters
markdown noise out of L3 repo-knowledge retrieval, adds PlanLinter H3 checks,
and tightens filesystem tool output/dir-listing behavior surfaced by prior
live QA (see project_readloop_campaign memory).
This commit is contained in:
2026-07-10 11:13:43 +04:00
parent f51a8dada4
commit 3d5e05c1fb
32 changed files with 742 additions and 85 deletions
@@ -7,7 +7,7 @@ import com.correx.core.journal.model.DecisionRecord
class DecisionJournalRenderer(private val keepLast: Int = 40) {
fun render(state: DecisionJournalState, summaryText: String? = null): String {
if (state.records.isEmpty() && summaryText == null) return ""
val visible = foldResolvedRetries(state.records)
val visible = foldResolvedRetries(state.records).filter { it.kind in AGENT_RELEVANT_KINDS }
return buildString {
appendLine("## Decision history (chronological — ground truth)")
if (summaryText != null) {
@@ -16,7 +16,7 @@ class DecisionJournalRenderer(private val keepLast: Int = 40) {
appendLine("+${state.lowSalienceOmittedCount} routine transitions/retries omitted")
}
}
visible.takeLast(keepLast).forEach { appendLine("- [${it.kind}] ${it.summary}") }
visible.takeLast(keepLast).forEach { appendLine("- ${it.summary}") }
}.trimEnd()
}
@@ -46,4 +46,15 @@ class DecisionJournalRenderer(private val keepLast: Int = 40) {
record.copy(summary = "$stage: $count retr${if (count == 1) "y" else "ies"}, resolved — stage completed")
}
}
companion object {
/**
* What actually shapes an agent's next decision: the goal, human steering, and failures
* worth not repeating. APPROVAL/ARTIFACT/TRANSITION are bookkeeping for the audit trail —
* agents don't act on "Approval APPROVED (tier T2)" or "Advanced x → y", so they're
* dropped from the render (still present in [DecisionJournalState.records] for the journal).
*/
private val AGENT_RELEVANT_KINDS =
setOf(DecisionKind.INTENT, DecisionKind.STEERING, DecisionKind.PREEMPT, DecisionKind.FAILURE, DecisionKind.RETRY)
}
}
@@ -27,11 +27,28 @@ class DecisionJournalRendererTest {
records = listOf(record(1), record(2)),
)
val result = renderer.render(state)
assertTrue(result.contains("- [INTENT] summary-1"))
assertTrue(result.contains("- [INTENT] summary-2"))
assertTrue(result.contains("- summary-1"))
assertTrue(result.contains("- summary-2"))
assertFalse(result.contains("omitted"))
}
@Test
fun `approval, artifact and transition records are excluded from the render`() {
val state = DecisionJournalState(
records = listOf(
record(1, DecisionKind.APPROVAL, "Approval APPROVED (tier T2)"),
record(2, DecisionKind.ARTIFACT, "Validated artifact x at stage y"),
record(3, DecisionKind.TRANSITION, "Advanced a → b (t1)").copy(stageId = "b", fromStageId = "a"),
record(4, DecisionKind.FAILURE, "Stage b failed: boom"),
),
)
val result = renderer.render(state)
assertFalse(result.contains("Approval APPROVED"))
assertFalse(result.contains("Validated artifact"))
assertFalse(result.contains("Advanced a"))
assertTrue(result.contains("- Stage b failed: boom"))
}
@Test
fun `summaryText appears before records`() {
val state = DecisionJournalState(
@@ -42,7 +59,7 @@ class DecisionJournalRendererTest {
val lines = result.lines()
val headerIdx = lines.indexOfFirst { it.startsWith("## Decision history") }
val summaryIdx = lines.indexOfFirst { it == "Compaction summary here" }
val recordIdx = lines.indexOfFirst { it.startsWith("- [INTENT]") }
val recordIdx = lines.indexOfFirst { it.startsWith("- summary") }
assertTrue(headerIdx >= 0)
assertTrue(summaryIdx > headerIdx)
assertTrue(recordIdx > summaryIdx)
@@ -75,7 +92,7 @@ class DecisionJournalRendererTest {
assertTrue(result.contains("## Decision history"))
assertTrue(result.contains("Compacted summary"))
assertTrue(result.contains("+2 routine transitions/retries omitted"))
assertFalse(result.contains("- ["))
assertFalse(result.contains("- "))
}
@Test
@@ -90,8 +107,8 @@ class DecisionJournalRendererTest {
)
val result = renderer.render(state)
assertFalse(result.contains("did not produce"), "resolved retry detail must not render")
assertTrue(result.contains("- [RETRY] scaffold: 2 retries, resolved — stage completed"))
assertTrue(result.contains("- [TRANSITION] Advanced scaffold → hook"))
assertTrue(result.contains("- scaffold: 2 retries, resolved — stage completed"))
assertFalse(result.contains("Advanced scaffold"), "TRANSITION records are audit-only, not agent-relevant")
}
@Test
@@ -102,7 +119,7 @@ class DecisionJournalRendererTest {
),
)
val result = renderer.render(state)
assertTrue(result.contains("- [RETRY] Retry 1/3 of hook: did not produce"))
assertTrue(result.contains("- Retry 1/3 of hook: did not produce"))
}
@Test
@@ -115,6 +132,6 @@ class DecisionJournalRendererTest {
),
)
val result = renderer.render(state)
assertTrue(result.contains("- [RETRY] Retry 1/3 of scaffold: regression"))
assertTrue(result.contains("- Retry 1/3 of scaffold: regression"))
}
}