{"record":{"id":"e70c042ab92c6bce","repo":"Hmbown/CodeWhale","slug":"invalid-auto-review-allow-index-action-kind-a","errorCode":null,"errorMessage":"Invalid auto_review.allow[{index}].action_kind '{action_kind}': this retired narrow kind cannot safely widen to a v0.9.8 decision class; replace it with an exact tool rule or a current action_kind.","messagePattern":"Invalid auto_review\\.allow\\[(.+?)\\]\\.action_kind '(.+?)': this retired narrow kind cannot safely widen to a v0\\.9\\.8 decision class; replace it with an exact tool rule or a current action_kind\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/config.rs","lineNumber":3052,"sourceCode":"        if !rule.has_matcher() {\n            anyhow::bail!(\n                \"Invalid auto_review.{kind}[{index}]: set at least one of tool or action_kind.\"\n            );\n        }\n        if let Some(action_kind) = rule.action_kind.as_deref() {\n            let normalized = action_kind.trim().to_ascii_lowercase().replace('-', \"_\");\n            if parse_auto_review_action_kind(&normalized).is_none() {\n                anyhow::bail!(\n                    \"Invalid auto_review.{kind}[{index}].action_kind '{action_kind}': expected read, write, shell, external, publish, or destructive.\"\n                );\n            }\n            if kind == \"allow\"\n                && !matches!(\n                    normalized.as_str(),\n                    \"read\" | \"write\" | \"shell\" | \"external\" | \"publish\" | \"destructive\"\n                )\n            {\n                anyhow::bail!(\n                    \"Invalid auto_review.allow[{index}].action_kind '{action_kind}': this retired narrow kind cannot safely widen to a v0.9.8 decision class; replace it with an exact tool rule or a current action_kind.\"\n                );\n            }\n        }\n    }\n    Ok(())\n}\n\nfn parse_auto_review_action_kind(raw: &str) -> Option<crate::tui::auto_review::ToolActionKind> {\n    match raw.trim().to_ascii_lowercase().replace('-', \"_\").as_str() {\n        \"read\" | \"mcp_read\" => Some(crate::tui::auto_review::ToolActionKind::Read),\n        \"write\" => Some(crate::tui::auto_review::ToolActionKind::Write),\n        \"shell\" => Some(crate::tui::auto_review::ToolActionKind::Shell),\n        \"external\" | \"network\" | \"git\" | \"mcp_action\" | \"browser\" | \"unknown\" => {\n            Some(crate::tui::auto_review::ToolActionKind::External)\n        }\n        \"publish\" => Some(crate::tui::auto_review::ToolActionKind::Publish),\n        \"destructive\" | \"secret\" => Some(crate::tui::auto_review::ToolActionKind::Destructive),","sourceCodeStart":3034,"sourceCodeEnd":3070,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/config.rs#L3034-L3070","documentation":"Specialized allow-rule guard (crates/tui/src/config.rs:3052): for kind == \"allow\", the normalized action_kind must be exactly one of read, write, shell, external, publish, destructive. Aliases that parse for other rule kinds (e.g. mcp_read) are rejected for allow, because a retired narrow allow rule cannot safely widen to a v0.9.8 decision class - widening would silently allow more than before.","triggerScenarios":"An upgraded config whose allow rules use alias action_kinds like mcp_read; hand-written allow rules copied from deny/ask examples where aliases are legal.","commonSituations":"Post-v0.9.8 migration of auto_review.allow sections; mixing rule templates between kinds.","solutions":["Replace the alias with the exact class (mcp_read -> read) or one of the other five current kinds","Or drop action_kind and pin the allow to an exact tool = \"<tool-id>\" rule","Prefer exact tool allows when in doubt - they can never widen"],"exampleFix":"# config.toml - before\n[[auto_review.allow]]\naction_kind = \"mcp_read\"\n\n# config.toml - after\n[[auto_review.allow]]\naction_kind = \"read\"\n# or narrower and safer:\n# tool = \"mcp__myserver__fetch\"\n","handlingStrategy":"validation","validationCode":"// Allow rules: exact classes only, no aliases\nconst ALLOW_KINDS: [&str; 6] = [\"read\",\"write\",\"shell\",\"external\",\"publish\",\"destructive\"];\nfn allow_kind_ok(k: &str) -> bool { ALLOW_KINDS.contains(&k.trim().to_ascii_lowercase().replace('-', \"_\").as_str()) }","typeGuard":"fn is_widened_allow_error(msg: &str) -> bool {\n    msg.contains(\"cannot safely widen to a v0.9.8 decision class\")\n}","tryCatchPattern":"// On migration errors, prefer the narrowest safe rewrite: exact tool allow\nif is_widened_allow_error(&e.to_string()) {\n    rewrite_rule_to_exact_tool(&mut rule); // tool = \"mcp__server__method\"; drop action_kind\n}","preventionTips":["Prefer exact tool matches for allow rules - they can never widen","After version upgrades, re-validate auto_review.allow sections first","Treat alias parsing as kind-specific: legal for deny/ask, rejected for allow"],"tags":["config","auto-review","validation","migration","security"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}