feat(kernel,artifacts): inject artifact-kind vocabulary into architect context
This commit is contained in:
@@ -3,6 +3,7 @@ package com.correx.core.artifacts.kind
|
||||
interface ArtifactKindRegistry {
|
||||
fun register(kind: ArtifactKind)
|
||||
fun get(id: String): ArtifactKind?
|
||||
fun list(): Collection<ArtifactKind>
|
||||
}
|
||||
|
||||
class DefaultArtifactKindRegistry : ArtifactKindRegistry {
|
||||
@@ -18,4 +19,6 @@ class DefaultArtifactKindRegistry : ArtifactKindRegistry {
|
||||
}
|
||||
|
||||
override fun get(id: String): ArtifactKind? = kinds[id]
|
||||
|
||||
override fun list(): Collection<ArtifactKind> = kinds.values.toList()
|
||||
}
|
||||
|
||||
+21
@@ -51,4 +51,25 @@ class ArtifactKindRegistryTest {
|
||||
assert("path" in schema.required)
|
||||
assert("content" in schema.required)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `list returns all registered kinds`() {
|
||||
val ids = registry.list().map { it.id }.toSet()
|
||||
assert("file_written" in ids) { "expected file_written in $ids" }
|
||||
assert("process_result" in ids) { "expected process_result in $ids" }
|
||||
assertEquals(2, ids.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `list reflects newly registered kind`() {
|
||||
val schema = JsonSchema(
|
||||
type = "object",
|
||||
properties = mapOf("x" to JsonSchemaProperty(type = "string")),
|
||||
required = listOf("x"),
|
||||
)
|
||||
registry.register(ConfigArtifactKind(id = "extra", schema = schema, llmEmitted = true))
|
||||
val ids = registry.list().map { it.id }.toSet()
|
||||
assert("extra" in ids) { "expected extra in $ids" }
|
||||
assertEquals(3, ids.size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.correx.core.kernel.orchestration
|
||||
// Context-entry builders kept top-level (not orchestrator members) for unit-testability
|
||||
// and to stay under detekt's function-count threshold (same pattern as isBackEdge).
|
||||
|
||||
import com.correx.core.artifacts.kind.ArtifactKind
|
||||
import com.correx.core.context.model.ContextEntry
|
||||
import com.correx.core.context.model.ContextLayer
|
||||
import com.correx.core.context.model.EntryRole
|
||||
@@ -47,6 +48,24 @@ fun criticArtifactIds(
|
||||
return graph.stages[fromStage]?.produces?.map { it.name }?.toSet() ?: emptySet()
|
||||
}
|
||||
|
||||
fun buildArtifactKindVocabularyEntry(kinds: Collection<ArtifactKind>): ContextEntry {
|
||||
val listing = kinds.joinToString(", ") { k ->
|
||||
if (k.llmEmitted) "${k.id} (llm-emitted)" else k.id
|
||||
}
|
||||
val content = "## Available artifact kinds\n" +
|
||||
"Every stage's \"produces\" MUST be exactly one of: $listing\n" +
|
||||
"Do not invent kinds — a plan referencing an unknown kind fails to compile."
|
||||
return ContextEntry(
|
||||
id = ContextEntryId(UUID.randomUUID().toString()),
|
||||
layer = ContextLayer.L1,
|
||||
content = content,
|
||||
sourceType = "artifactKindVocabulary",
|
||||
sourceId = "artifact-kinds",
|
||||
tokenEstimate = content.length / 4,
|
||||
role = EntryRole.SYSTEM,
|
||||
)
|
||||
}
|
||||
|
||||
fun buildProjectProfileEntry(profile: BoundProjectProfile): ContextEntry {
|
||||
val content = buildString {
|
||||
append("## Project profile\n")
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@ import com.correx.core.inference.Tokenizer
|
||||
import com.correx.core.journal.DefaultDecisionJournalRepository
|
||||
import com.correx.core.journal.DecisionJournalRenderer
|
||||
import com.correx.core.artifacts.ArtifactState
|
||||
import com.correx.core.artifacts.kind.ArtifactKindRegistry
|
||||
import com.correx.core.artifactstore.ArtifactStore
|
||||
import com.correx.core.events.events.ApprovalDecisionResolvedEvent
|
||||
import com.correx.core.events.events.ApprovalRequestedEvent
|
||||
@@ -55,7 +56,8 @@ class DefaultSessionOrchestrator(
|
||||
tokenizer: Tokenizer? = null,
|
||||
private val decisionJournalRepository: DefaultDecisionJournalRepository,
|
||||
private val compactionService: JournalCompactionService? = null,
|
||||
) : SessionOrchestrator(repositories, engines, artifactStore, decisionJournalRepository), ApprovalGateway {
|
||||
artifactKindRegistry: ArtifactKindRegistry? = null,
|
||||
) : SessionOrchestrator(repositories, engines, artifactStore, decisionJournalRepository, artifactKindRegistry = artifactKindRegistry), ApprovalGateway {
|
||||
override val tokenizer: Tokenizer? = tokenizer
|
||||
override val cancellations: ConcurrentHashMap<SessionId, AtomicBoolean> =
|
||||
ConcurrentHashMap<SessionId, AtomicBoolean>()
|
||||
|
||||
+6
-1
@@ -11,6 +11,7 @@ import com.correx.core.approvals.model.ApprovalDecision
|
||||
import com.correx.core.approvals.model.ApprovalScopeIdentity
|
||||
import com.correx.core.approvals.model.DomainApprovalRequest
|
||||
import com.correx.core.artifacts.ArtifactState
|
||||
import com.correx.core.artifacts.kind.ArtifactKindRegistry
|
||||
import com.correx.core.artifacts.kind.JsonSchema
|
||||
import com.correx.core.artifacts.kind.FileWrittenArtifact
|
||||
import com.correx.core.artifacts.kind.ProcessResultArtifact
|
||||
@@ -153,6 +154,7 @@ abstract class SessionOrchestrator(
|
||||
protected val artifactStore: ArtifactStore,
|
||||
private val decisionJournalRepository: DefaultDecisionJournalRepository,
|
||||
private val decisionJournalRenderer: DecisionJournalRenderer = DecisionJournalRenderer(),
|
||||
private val artifactKindRegistry: ArtifactKindRegistry? = null,
|
||||
) {
|
||||
private val log = LoggerFactory.getLogger(this::class.java)
|
||||
private val eventStore: EventStore = repositories.eventStore
|
||||
@@ -374,9 +376,12 @@ abstract class SessionOrchestrator(
|
||||
?.let { listOf(buildProjectProfileEntry(it)) } ?: emptyList()
|
||||
val retryFeedbackEntries = buildRetryFeedbackEntry(sessionEvents, stageId)
|
||||
?.let { listOf(it) } ?: emptyList()
|
||||
val vocabularyEntries = artifactKindRegistry
|
||||
?.takeIf { stageConfig.metadata["injectArtifactKinds"] == "true" }
|
||||
?.let { listOf(buildArtifactKindVocabularyEntry(it.list())) } ?: emptyList()
|
||||
var accumulatedEntries =
|
||||
systemPrompt + profileEntries + projectProfileEntries + journalEntries + repoMapEntries +
|
||||
needsEntries + schemaEntries + promptEntries + steeringEntries + retryFeedbackEntries
|
||||
needsEntries + schemaEntries + vocabularyEntries + promptEntries + steeringEntries + retryFeedbackEntries
|
||||
val contextPack = contextPackBuilder.build(
|
||||
id = ContextPackId(UUID.randomUUID().toString()),
|
||||
sessionId = sessionId,
|
||||
|
||||
Reference in New Issue
Block a user