fix(context): render the stage role prompt as the system prompt (#416)

The role prompt was an L1/USER entry, so PromptRenderer emitted it as a user
turn behind the intent, decision journal, repo map and docs catalog, outranked
by the pinned schemaInstruction it contradicts. It is now L0/SYSTEM and folds
into the leading system message, and it sits directly after systemPrompt in
assembly so it heads the system block rather than trailing schemaEntries.

Extracted buildAgentPromptEntry so both the promptInline and prompt-path
branches build the entry one way. Guard test mutation-verified.

#416's finding 1 was wrong: the role prompt was never evictable. "agentPrompt"
is already in REQUIRED_SOURCE_TYPES, and DefaultContextPackBuilder exempts
REQUIRED entries from pruning at any layer. Layer was never the pinning
mechanism here; message placement was the whole defect.

Also pins groundingFeedback and recoveryTicket, the two feedback types that
were neither REQUIRED nor in neverDropSourceTypes. The recovery stage exists
only because of its ticket, so pruning the ticket left it nothing to repair.

Not done: reconciling the pinned schemaInstruction ("respond with JSON only")
against the role prompt's emit_artifact instruction, and live verification
across analyst/architect/role_pipeline.toml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 00:57:58 +04:00
parent d1b84a1a9f
commit a9196a0037
4 changed files with 60 additions and 26 deletions
@@ -26,6 +26,7 @@ import com.correx.core.events.types.StageId
import com.correx.core.events.types.TransitionId
import com.correx.core.events.events.RepoKnowledgeHit
import com.correx.core.kernel.orchestration.buildAgentInstructionsEntry
import com.correx.core.kernel.orchestration.buildAgentPromptEntry
import com.correx.core.kernel.orchestration.buildArtifactKindVocabularyEntry
import com.correx.core.kernel.orchestration.buildProjectProfileEntry
import com.correx.core.kernel.orchestration.buildRelevantFilesEntry
@@ -194,6 +195,19 @@ class ContextFeedbackTest {
assertEquals("projectProfile", entry.sourceType)
}
@Test
fun `stage role prompt renders as the system prompt, not a user turn`() {
// #416: as L1/USER this arrived as a user message behind the intent, journal, repo map and docs
// catalog, outranked by the pinned schemaInstruction that contradicts it. Live session fced377e:
// discovery never acknowledged its role and drifted into implementation.
val entry = buildAgentPromptEntry("You discover. You do not implement.", StageId("discovery"), 7)
assertEquals(ContextLayer.L0, entry.layer)
assertEquals(EntryRole.SYSTEM, entry.role)
assertEquals("agentPrompt", entry.sourceType)
assertEquals("discovery", entry.sourceId)
assertEquals("You discover. You do not implement.", entry.content)
}
@Test
fun `agent instructions render as single L0 entry`() {
val entry = buildAgentInstructionsEntry(