feat(stage-workingset): durable, replay-safe stage working set (slices 1-4)

Stage agents were spending most of their turn budget re-discovering files
already known from prior stages/attempts. Root cause: nothing durable carries
acquired knowledge across handoffs and retries. First four slices of the fix:

- core:sourcedesc — new dependency-free module: describe(path, bytes) derives
  comment-free structural navigation metadata (module, bounded symbols, bounded
  imports, versioned format). Deliberately non-prose: descriptors are derived
  from agent-writable files and rendered into successor-stage context, so
  comments/docstrings/literals are excluded to close a prompt-injection channel.
  CAS post-image hash stays authoritative; descriptor is disposable navigation.

- kernel retry-repair state (ContextFeedback): on retry, name the authoritative
  CAS images of files this stage already wrote so the agent patches them instead
  of re-reading to rediscover them.

- kernel file-written manifest (SessionOrchestratorArtifacts): each produced
  file surfaced with its authoritative CAS image plus a comment-free structural
  descriptor (via core:sourcedesc, over recorded CAS bytes — replay-safe).

- apps/server RepoMapIndexer: route the injected repo-map descriptor through the
  comment-free describe(). Previously scraped leading comments, which were
  embedded into L3 and surfaced verbatim to successor stages — an injection
  channel from one stage into the next. Structural facts (module + imports +
  symbols) remain as the retrieval signal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 14:22:12 +04:00
parent 159b3f1eb9
commit 918f2f5652
11 changed files with 309 additions and 48 deletions
@@ -4,8 +4,13 @@ import com.correx.core.artifacts.kind.JsonSchema
import com.correx.core.artifacts.kind.TypedArtifactSlot
import com.correx.core.context.model.ContextLayer
import com.correx.core.context.model.EntryRole
import com.correx.core.events.events.FileWrittenEvent
import com.correx.core.events.events.RefinementIterationEvent
import com.correx.core.events.events.RetryAttemptedEvent
import com.correx.core.events.events.ToolInvocationRequestedEvent
import com.correx.core.events.events.ToolRequest
import com.correx.core.events.types.ToolInvocationId
import com.correx.core.approvals.Tier
import com.correx.core.events.types.ArtifactId
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
@@ -50,6 +55,43 @@ class ContextFeedbackTest {
assertTrue(entry.content.contains("tests failed: NPE in FooTest"), "content: ${entry.content}")
}
@Test
fun `retry repair state names authoritative CAS images of files this stage already wrote`() {
val inv = ToolInvocationId("inv-1")
val events = listOf(
stored(
payload = ToolInvocationRequestedEvent(
invocationId = inv, sessionId = SessionId("s"), stageId = StageId("impl"),
toolName = "file_write", tier = Tier.T2,
request = ToolRequest(
invocationId = inv, sessionId = SessionId("s"), stageId = StageId("impl"),
toolName = "file_write", parameters = mapOf("path" to "frontend/src/hooks/queries.ts"),
),
),
),
stored(
payload = FileWrittenEvent(
invocationId = inv, sessionId = SessionId("s"), path = "frontend/src/hooks/queries.ts",
postImageHash = "cafebabe", preExisted = true, timestampMs = 0,
),
),
stored(
payload = RetryAttemptedEvent(
SessionId("s"), StageId("impl"), 2, 3,
"build gate: queries.ts(39,3): error TS1005: '}' expected", gate = "execution",
),
),
)
val entry = buildRetryFeedbackEntry(events, StageId("impl"))!!
assertTrue(entry.content.contains("## Retry repair state"), "content: ${entry.content}")
assertTrue(entry.content.contains("gate 'execution'"), "content: ${entry.content}")
assertTrue(
entry.content.contains("frontend/src/hooks/queries.ts — CAS cafebabe"),
"content: ${entry.content}",
)
assertTrue(entry.content.contains("do NOT re-read"), "content: ${entry.content}")
}
@Test
fun `retry entry for other stage is not returned for queried stage`() {
val events = listOf(