{"record":{"id":"f6f318148d9ca46b","repo":"Hmbown/CodeWhale","slug":"fleet-authority-mismatch-at-the-spawn-boundary-th","errorCode":null,"errorMessage":"fleet authority mismatch at the spawn boundary: the receipt names {key}=`{expected_value}` but the child would be constructed with `{actual}`. Refusing the spawn — a Fleet ceiling that does not reach the runtime is not a ceiling.","messagePattern":"fleet authority mismatch at the spawn boundary: the receipt names (.+?)=`(.+?)` but the child would be constructed with `(.+?)`\\. Refusing the spawn — a Fleet ceiling that does not reach the runtime is not a ceiling\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":9635,"sourceCode":"    let actual_write = input\n        .get(\"write_authority\")\n        .and_then(Value::as_str)\n        .unwrap_or(\"read_only\");\n    let actual_depth = input\n        .get(\"max_depth\")\n        .and_then(Value::as_u64)\n        .map(|depth| depth.to_string())\n        .unwrap_or_default();\n\n    for (key, actual) in [\n        (\"write\", actual_write.to_string()),\n        (\"depth\", actual_depth),\n        (\"allow\", actual_allow),\n        (\"deny\", listed(\"disallowed_tools\")),\n    ] {\n        let expected_value = fields.get(key).copied().unwrap_or_default();\n        if expected_value != actual {\n            return Err(anyhow!(\n                \"fleet authority mismatch at the spawn boundary: the receipt names {key}=`{expected_value}` \\\n                 but the child would be constructed with `{actual}`. Refusing the spawn — a Fleet \\\n                 ceiling that does not reach the runtime is not a ceiling.\"\n            ));\n        }\n    }\n    Ok(())\n}\n\n// === Sub-agent Execution ===\n\n/// Build the system prompt for a sub-agent.\n///\n/// Starts with the per-type prompt (`FleetRole::system_prompt`) and\n/// appends a one-line role overlay when `assignment.role` is set. The\n/// full role library — TOML overlays from `~/.deepseek/roles/`, the\n/// `/roles` slash command, model overrides per role — lands in 0.6.7.\n/// For 0.6.6 we just don't drop the role on the floor: the model sees","sourceCodeStart":9617,"sourceCodeEnd":9653,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/subagent/mod.rs#L9617-L9653","documentation":"At the spawn boundary, `verify_fleet_authority_input` compares the actual spawn input just built against the receipt's fingerprint for four fields: write (write ceiling), depth (max_depth), allow (allowed_tools list), deny (disallowed_tools list). A mismatch means the receipt authorizes a different child surface than the one about to launch, so the spawn fails closed (mapped to ToolError::permission_denied). The wire form is the source of truth because it is what the child is actually constructed from.","triggerScenarios":"spawn_workflow_task where request.allowed_tools / disallowed_tools / max_depth / write flag diverge from the receipt: policy edited after the receipt was minted, defaults applied between minting and spawn, or host-derived ceilings (e.g. network_tool=false) converted to deny lists the receipt didn't name.","commonSituations":"Editing a Fleet's allow/deny lists after issuing receipts; replaying old receipts against a build with new defaults; code paths that add or filter tools between receipt creation and spawn; stale identity records attached to retried tasks.","solutions":["Re-issue the receipt (re-run the Fleet/workflow step that mints it) after any policy or option change, so the fingerprint matches the current spawn input.","Diff the four fields named by the error (write, depth, allow, deny) to find which one the spawn added, dropped, or reordered.","Do not mutate allowed_tools/disallowed_tools/max_depth between receipt creation and spawn_workflow_task.","Never reuse persisted receipts across builds whose defaults changed — regenerate them."],"exampleFix":"// before: receipt minted, then tools mutated before spawn\nrequest.allowed_tools = Some(vec![\"read\".into()]); // diverges from receipt\nspawn_workflow_task(identity, request).await?; // permission_denied (Err 1218)\n\n// after: mint receipt from the same request you spawn with\nlet receipt = authority.fingerprint_for(&request);\nlet identity = Identity { fleet_authority_fingerprint: Some(receipt), .. };\nspawn_workflow_task(identity, request).await?;","handlingStrategy":"try-catch","validationCode":"// Re-run the same comparison before spawn to fail with a clear diagnostic:\nif let Some(expected) = identity.fleet_authority_fingerprint.as_deref() {\n    verify_fleet_authority_input(expected, &input)\n        .context(\"receipt/spawn divergence — re-issue the receipt after policy changes\")?;\n}","typeGuard":"fn spawn_matches_receipt(expected: &str, input: &serde_json::Value) -> bool {\n    verify_fleet_authority_input(expected, input).is_ok()\n}","tryCatchPattern":"match spawn_workflow_task(identity, request).await {\n    Err(ToolError::PermissionDenied(msg)) if msg.contains(\"fleet authority mismatch\") => {\n        // fail-closed spawn: diff write/depth/allow/deny vs the receipt, re-mint, retry once\n    }\n    r => r?,\n}","preventionTips":["Mint the receipt from the exact request object you will spawn with.","Never mutate allowed_tools/disallowed_tools/max_depth between minting and spawn.","Re-issue receipts after any Fleet policy edit or build upgrade.","Never bypass or strip the fingerprint to make a spawn pass."],"tags":["subagent","fleet","fingerprint","authorization","fail-closed","rust"],"backgroundTag":"fingerprint-mismatch","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}