Stop repo lanes from executing the wrong task payload

The next repo-local sweep target was ROADMAP #71: a claw-code lane
accepted an unrelated KakaoTalk/image-analysis prompt even though the
lane itself was supposed to be repo-scoped work. This extends the
existing prompt-misdelivery guardrail with an optional structured task
receipt so worker boot can reject visible wrong-task context before the
lane continues executing.

Constraint: Keep the fix inside the existing worker_boot / WorkerSendPrompt control surface instead of inventing a new external OMX-only protocol
Rejected: Treat wrong-task receipts as generic shell misdelivery | loses the expected-vs-observed task context needed to debug contaminated lanes
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If task-receipt fields change later, update the WorkerSendPrompt schema, worker payload serialization, and wrong-task regression together
Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace; architect review APPROVE
Not-tested: External orchestrators that have not yet started populating the optional task_receipt field
This commit is contained in:
Yeachan-Heo
2026-04-12 07:00:07 +00:00
parent 3b806702e7
commit f309ff8642
4 changed files with 195 additions and 12 deletions

View File

@@ -20,7 +20,7 @@ use runtime::{
summary_compression::compress_summary_text,
task_registry::TaskRegistry,
team_cron_registry::{CronRegistry, TeamRegistry},
worker_boot::{WorkerReadySnapshot, WorkerRegistry},
worker_boot::{WorkerReadySnapshot, WorkerRegistry, WorkerTaskReceipt},
write_file, ApiClient, ApiRequest, AssistantEvent, BashCommandInput, BashCommandOutput,
BranchFreshness, ConfigLoader, ContentBlock, ConversationMessage, ConversationRuntime,
GrepSearchInput, LaneCommitProvenance, LaneEvent, LaneEventBlocker, LaneEventName,
@@ -930,7 +930,22 @@ pub fn mvp_tool_specs() -> Vec<ToolSpec> {
"type": "object",
"properties": {
"worker_id": { "type": "string" },
"prompt": { "type": "string" }
"prompt": { "type": "string" },
"task_receipt": {
"type": "object",
"properties": {
"repo": { "type": "string" },
"task_kind": { "type": "string" },
"source_surface": { "type": "string" },
"expected_artifacts": {
"type": "array",
"items": { "type": "string" }
},
"objective_preview": { "type": "string" }
},
"required": ["repo", "task_kind", "source_surface", "objective_preview"],
"additionalProperties": false
}
},
"required": ["worker_id"],
"additionalProperties": false
@@ -1522,7 +1537,11 @@ fn run_worker_await_ready(input: WorkerIdInput) -> Result<String, String> {
#[allow(clippy::needless_pass_by_value)]
fn run_worker_send_prompt(input: WorkerSendPromptInput) -> Result<String, String> {
let worker = global_worker_registry().send_prompt(&input.worker_id, input.prompt.as_deref())?;
let worker = global_worker_registry().send_prompt(
&input.worker_id,
input.prompt.as_deref(),
input.task_receipt,
)?;
to_pretty_json(worker)
}
@@ -2439,6 +2458,8 @@ struct WorkerSendPromptInput {
worker_id: String,
#[serde(default)]
prompt: Option<String>,
#[serde(default)]
task_receipt: Option<WorkerTaskReceipt>,
}
const fn default_auto_recover_prompt_misdelivery() -> bool {