refactor: decomposition WIP (orchestrator/server/execution-plan)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DhFXmKe4WisSSPf9LrmmTg
This commit is contained in:
2026-07-15 13:17:01 +04:00
parent 9b925e141d
commit d69cb12ce9
17 changed files with 209 additions and 20 deletions
@@ -24,6 +24,7 @@ class FileSystemContractEvaluator : ContractAssertionEvaluator {
// allowComments/allowTrailingComma make tsconfig.json (JSONC — comments + trailing commas are
// legal and tsc/Vite parse them) pass valid_json instead of false-failing a valid config file.
@OptIn(kotlinx.serialization.ExperimentalSerializationApi::class)
private val json = Json {
ignoreUnknownKeys = true
isLenient = true
@@ -16,6 +16,7 @@ import com.correx.core.events.events.RepoMapComputedEvent
import com.correx.core.events.events.OrchestrationPausedEvent
import com.correx.core.events.events.OrchestrationResumedEvent
import com.correx.core.events.events.ExecutionPlanLockedEvent
import com.correx.core.events.events.InitialIntentEvent
import com.correx.core.events.events.SteeringNoteAddedEvent
import com.correx.core.events.types.ArtifactId
import com.correx.core.events.types.ClarificationRequestId
@@ -120,8 +121,43 @@ internal suspend fun SessionOrchestrator.buildSteeringNoteEntries(sessionId: Ses
}
/**
* Injects the operator's answers to a stage's open questions as an L2 USER entry, so the stage
* sees its own questions resolved on the clarification re-run. Correlates each answer's
* Injects the initial user intent (the freeform request that started the run) as a pinned L0
* SYSTEM entry present in EVERY stage's context (architecture-conformance, 2026-07-14). The intent
* is the single most load-bearing constraint of a run, yet it previously reached normal stages only
* as a repo-map retrieval seed (repoKnowledgeQuery) or, on the rare Tier-2 recovery path, the
* arbiter ticket — so an implementer could drift from the goal with the goal itself absent from its
* authoritative context. Standing at L0/SYSTEM it is weighted as an instruction and never dropped
* under budget. Absent for fixed-task workflows (no InitialIntentEvent) → empty.
*/
internal suspend fun SessionOrchestrator.buildIntentEntry(sessionId: SessionId): List<ContextEntry> {
val intent = initialIntent(sessionId)?.takeIf { it.isNotBlank() } ?: return emptyList()
val content = "## Authoritative intent\nThe operator's request that started this run. Everything " +
"you produce must serve it; do not drift from it or expand its scope:\n$intent"
return listOf(
ContextEntry(
id = ContextEntryId(UUID.randomUUID().toString()),
layer = ContextLayer.L0,
content = content,
sourceType = "initialIntent",
sourceId = sessionId.value,
tokenEstimate = estimateTokens(content),
role = EntryRole.SYSTEM,
),
)
}
/** The freeform request that started the run, or null for a fixed-task workflow. */
internal fun SessionOrchestrator.initialIntent(sessionId: SessionId): String? =
eventStore.read(sessionId)
.mapNotNull { it.payload as? InitialIntentEvent }
.firstOrNull()?.intent?.trim()
/**
* Injects the operator's answers to a stage's open questions as a pinned L0 SYSTEM entry, so the
* stage sees its own questions resolved on the clarification re-run. The prompt calls these answers
* "authoritative", so they are placed as authoritative standing instructions (L0/SYSTEM), not a
* droppable L2 USER turn — matching the mechanics to the stated authority. Correlates each answer's
* questionId back to the prompt recorded on the [ClarificationRequestedEvent].
*/
@@ -141,12 +177,12 @@ internal suspend fun SessionOrchestrator.buildClarificationAnswerEntries(session
return listOf(
ContextEntry(
id = ContextEntryId(UUID.randomUUID().toString()),
layer = ContextLayer.L2,
layer = ContextLayer.L0,
content = content,
sourceType = "clarificationAnswer",
sourceId = sessionId.value,
tokenEstimate = estimateTokens(content),
role = EntryRole.USER,
role = EntryRole.SYSTEM,
),
)
}
@@ -144,6 +144,7 @@ internal suspend fun SessionOrchestrator.executeStage(
?: ResponseFormat.Text
val schemaEntries = buildSchemaEntries(responseFormat, stageId)
val intentEntries = buildIntentEntry(sessionId)
val steeringEntries = buildSteeringNoteEntries(sessionId)
val clarificationEntries = buildClarificationAnswerEntries(sessionId)
@@ -238,7 +239,7 @@ internal suspend fun SessionOrchestrator.executeStage(
?.let { buildRemainingDeltaEntry(contractFailureItems(it)) }
?.let { listOf(it) } ?: emptyList()
var accumulatedEntries = stampBuckets(
systemPrompt + profileEntries + projectProfileEntries + agentInstructionsEntries +
systemPrompt + intentEntries + profileEntries + projectProfileEntries + agentInstructionsEntries +
journalEntries + repoMapEntries + claimedTaskEntries +
needsEntries + schemaEntries + vocabularyEntries + promptEntries + steeringEntries +
clarificationEntries + retryFeedbackEntries + recoveryTicketEntries + remainingDeltaEntries,
@@ -190,11 +190,22 @@ internal suspend fun SessionOrchestrator.runReviewGate(
val files = stageWrittenPaths(sessionId, stageId)
if (files.isEmpty()) return StageExecutionResult.Success(emptyList())
val objective = stageConfig.needs
.mapNotNull { artifactContentCache["${sessionId.value}:${it.value}"] }
.joinToString("\n\n")
.ifBlank { "Stage '${stageId.value}' produced the files below; review them for defects." }
.take(REVIEW_OBJECTIVE_CAP)
// Goal-conformance framing (architecture-conformance 2026-07-14): lead the reviewer with the
// authoritative intent and the stage's declared scope so it can judge whether the files serve
// the goal (and stay within scope), not just whether they are internally defect-free — the
// deterministic gates already cover mechanical correctness. Absent intent/scope degrade to the
// prior upstream-artifact objective.
val intentHeader = initialIntent(sessionId)?.takeIf { it.isNotBlank() }
?.let { "## Authoritative intent (the goal these files must serve)\n$it\n\n" } ?: ""
val scopeHeader = stageConfig.touches.takeIf { it.isNotEmpty() }
?.let { "## This stage's declared scope\n${it.joinToString(", ")}\n\n" } ?: ""
val objective = (
intentHeader + scopeHeader +
stageConfig.needs
.mapNotNull { artifactContentCache["${sessionId.value}:${it.value}"] }
.joinToString("\n\n")
.ifBlank { "Stage '${stageId.value}' produced the files below; review them for defects." }
).take(REVIEW_OBJECTIVE_CAP)
val outcome = reviewer.review(sessionId, stageId, workspaceRoot, files, objective)
@@ -38,6 +38,14 @@ data class StageConfig(
// FAILS the gate — catching missing files, which the runtime write-manifest alone (it only
// inspects files that WERE written) cannot. Derived from the concrete entries of PlanStage.writes.
val expectedFiles: List<String> = emptyList(),
// Declared scope for this stage (architecture-conformance, design 2026-07-14): the
// workspace-relative globs the stage's intent is confined to (e.g. a frontend-client stage
// declares `frontend/**`). Distinct from [writeManifest], which is DERIVED from the concrete
// `writes` and so cannot contradict them: `touches` is the architect's separate statement of
// intent, and the plan compiler rejects a plan whose declared `writes` escape it (scope creep
// caught at plan time, before any code is written). Also fed to the semantic reviewer so it can
// judge goal-conformance against the declared scope. Empty = no scope constraint (default).
val touches: List<String> = emptyList(),
// Opt-in to the Gate 3 semantic (LLM) reviewer for this stage (staged-verification § Gate 3). When
// true and a reviewer is wired, the reviewer reads this stage's produced files after the
// deterministic funnel passes and raises advisory PR-comment findings; a high-confidence