{"record":{"id":"2a2a3180d85c89ce","repo":"Hmbown/CodeWhale","slug":"task-admission-replay-does-not-match-the-bound-request","errorCode":null,"errorMessage":"Task admission replay does not match the bound request","messagePattern":"Task admission replay does not match the bound request","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/task_manager.rs","lineNumber":3134,"sourceCode":"            .as_ref()\n            .is_some_and(|value| value != &task.workspace)\n        || request\n            .mode\n            .as_ref()\n            .is_some_and(|value| value != &task.mode)\n        || request\n            .allow_shell\n            .is_some_and(|value| value != task.allow_shell)\n        || request\n            .trust_mode\n            .is_some_and(|value| value != task.trust_mode)\n        || request\n            .permission_posture\n            .as_deref()\n            .is_some_and(|value| Some(value) != task.permission_posture.as_deref())\n        || task.auto_approve != request.auto_approve.unwrap_or(false)\n    {\n        bail!(\"Task admission replay does not match the bound request\");\n    }\n    Ok(())\n}\n\n/// A read-only inventory and reconstructed queue. Execution recovery is a\n/// separate transaction requiring proof that a particular generation died.\nstruct LoadedTaskState {\n    tasks: HashMap<String, TaskRecord>,\n    queue: VecDeque<String>,\n}\n\nfn load_state(tasks_dir: &Path, queue_path: &Path) -> Result<LoadedTaskState> {\n    let mut tasks = HashMap::new();\n    if tasks_dir.exists() {\n        for entry in fs::read_dir(tasks_dir)\n            .with_context(|| format!(\"Failed to read tasks dir {}\", tasks_dir.display()))?\n        {\n            let entry = entry?;","sourceCodeStart":3116,"sourceCodeEnd":3152,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/task_manager.rs#L3116-L3152","documentation":"validate_bound_task_request re-checks a replayed admission request against the durable TaskRecord: title/name, cwd, permission_posture, and auto_approve must match exactly (with the same defaults applied). A mismatch means the request would re-admit a different task than the one durably bound, so it is rejected.","triggerScenarios":"Calling validate_bound_task_request (via admission/recovery) with a NewTaskRequest whose permission_posture differs from the record's, whose auto_approve differs from the record's default, or with any other non-matching bound field.","commonSituations":"Replaying a saved request after the user changed approval posture or cwd; constructing the request manually with defaults that differ from the stored record; resubmitting an old request against a newer record version.","solutions":["Replay the exact original request (same permission_posture, auto_approve, cwd, title) that produced the record","If intent changed, create a new task instead of replaying admission","Compare the record's fields first and reconcile the request before validating"],"exampleFix":"// before\nlet req = NewTaskRequest { auto_approve: Some(true), ..orig };\nvalidate(&task, &req)?; // mismatch\n// after\nlet req = NewTaskRequest { auto_approve: Some(task.auto_approve), ..orig };\nvalidate(&task, &req)?;","handlingStrategy":"validation","validationCode":"if request.auto_approve.unwrap_or(false) != task.auto_approve\n    || request.permission_posture.as_deref().unwrap_or_default() != task.permission_posture.as_deref().unwrap_or_default() {\n    return Err(anyhow!(\"request would re-admit a different task\"));\n}","typeGuard":"fn replay_matches(task: &TaskRecord, req: &NewTaskRequest) -> bool {\n    req.permission_posture.as_deref().map(|p| Some(p) == task.permission_posture.as_deref()).unwrap_or(true)\n        && req.auto_approve.unwrap_or(false) == task.auto_approve\n}","tryCatchPattern":"match manager.admit_bound_task(&task, &request).await {\n    Err(e) if e.to_string().contains(\"admission replay does not match\") => {\n        // intent changed: create a fresh task instead of replaying\n        manager.create_task(request.into_fresh()).await?;\n    }\n    other => other?,\n}","preventionTips":["Persist the original request alongside the record and replay it verbatim","Treat replayed admission as immutable: never alter posture/approval fields","Prefer create-new over replay when any intent changed"],"tags":["validation","admission","replay","task-manager"],"backgroundTag":"schema-validation-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}