feat(workflow): reject execution plans that reference unknown tools

A freestyle stage's tools are LLM-authored in the execution_plan. At run time an
unresolvable tool name is silently dropped (resolve -> null -> mapNotNull), so the
stage runs toolless — e.g. a misspelled task_update means the implementation stage
quietly loses task tracking, invisible without a live run.

ExecutionPlanCompiler now takes the registered-tool universe and rejects a plan that
names an unknown tool, with a message identifying the tool and stage. FreestyleDriver
already turns a compile failure into a rejection the architect retries. The param
defaults to empty (validation skipped) so existing callers are unaffected; Main feeds
it toolRegistry.all().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 16:16:03 +00:00
parent 12d5b9d7dc
commit d12d64e4fe
3 changed files with 73 additions and 1 deletions
@@ -457,7 +457,7 @@ fun main() {
val freestyleDriver = FreestyleDriver(
eventStore = eventStore,
compiler = ExecutionPlanCompiler(artifactKindRegistry),
compiler = ExecutionPlanCompiler(artifactKindRegistry, toolRegistry.all().map { it.name }.toSet()),
planContent = { sid -> orchestrator.validatedArtifactContent(sid, ArtifactId("execution_plan")) },
config = defaultOrchestrationConfig,
runPhase2 = { sid, graph, cfg -> orchestrator.run(sid, graph, cfg) },