{"record":{"id":"1e07ed5605f9e72a","repo":"Hmbown/CodeWhale","slug":"invalid-auto-review-kind-index-set-at-least","errorCode":null,"errorMessage":"Invalid auto_review.{kind}[{index}]: set at least one of tool or action_kind.","messagePattern":"Invalid auto_review\\.(.+?)\\[(.+?)\\]: set at least one of tool or action_kind\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/config.rs","lineNumber":3035,"sourceCode":"                .action_kind\n                .as_deref()\n                .is_some_and(|value| !value.trim().is_empty())\n    }\n}\n\nfn validate_auto_review_rules(kind: &str, rules: &[AutoReviewRuleConfig]) -> Result<()> {\n    for (index, rule) in rules.iter().enumerate() {\n        if rule\n            .text_contains\n            .as_deref()\n            .is_some_and(|value| !value.trim().is_empty())\n        {\n            anyhow::bail!(\n                \"Invalid auto_review.{kind}[{index}].text_contains: user-intent matching was retired; scope the rule with tool and/or action_kind.\"\n            );\n        }\n        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.\"","sourceCodeStart":3017,"sourceCodeEnd":3053,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/config.rs#L3017-L3053","documentation":"An auto_review rule has no matcher at all: neither tool nor action_kind is set (has_matcher() false, crates/tui/src/config.rs:3035). Such a rule would match nothing (or everything, depending on interpretation), so validation rejects it at config load.","triggerScenarios":"A rule that only had text_contains (already stripped by the earlier check), a rule written with only metadata like review_level or comment, or a typo'd key that silently deserialized to nothing.","commonSituations":"Migrating old configs rule-by-rule; typos in key names (tools instead of tool) that serde ignores.","solutions":["Add tool = \"<tool-id>\" or action_kind = \"<kind>\" to the rule","Check for typo'd keys (tool vs tools) that leave the matcher empty","Remove the rule entirely if it was vestigial"],"exampleFix":"# config.toml - before\n[[auto_review.ask]]\ncomment = \"check pushes\"\n\n# config.toml - after\n[[auto_review.ask]]\ntool = \"bash\"\naction_kind = \"publish\"\n","handlingStrategy":"validation","validationCode":"fn rule_has_matcher(r: &AutoReviewRuleConfig) -> bool {\n    r.tool.as_deref().is_some_and(|t| !t.trim().is_empty())\n        || r.action_kind.is_some()\n}","typeGuard":"fn is_matcherless_rule_error(msg: &str) -> bool {\n    msg.contains(\"set at least one of tool or action_kind\")\n}","tryCatchPattern":"// At config-build time, drop or fix matcherless rules explicitly\nrules.retain(|r| {\n    if !rule_has_matcher(r) { warn!(\"dropping matcherless rule {r:?}\"); false } else { true }\n});","preventionTips":["Watch for typo'd keys (tools vs tool) that serde silently ignores","Template every new rule with a tool or action_kind from the start"],"tags":["config","auto-review","validation"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}