{"record":{"id":"694f0b3881c72786","repo":"Humanizr/Humanizer","slug":"invariant-bundle-cannot-define-productive-rules","errorCode":null,"errorMessage":"Invariant bundle cannot define productive rules.","messagePattern":"Invariant bundle cannot define productive rules\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/InflectionCatalogValidation.cs","lineNumber":164,"sourceCode":"                throw new InvalidOperationException(\n                    $\"Inflection rule '{ruleId}' output must contain exactly one bounded '{{stem}}' placeholder.\");\n            }\n        }\n\n        static void ValidateInvariantCapability(\n            string capability,\n            string casing,\n            ImmutableArray<InflectionLexemeInput> lexemes,\n            ImmutableArray<InflectionRuleInput> rules)\n        {\n            if (capability != \"invariant\")\n            {\n                return;\n            }\n\n            if (!rules.IsEmpty)\n            {\n                throw new InvalidOperationException(\n                    \"Invariant bundle cannot define productive rules.\");\n            }\n\n            foreach (var lexeme in lexemes)\n            {\n                var forms = lexeme.Singular.Accepted\n                    .Concat(lexeme.DictionaryPlural.Accepted)\n                    .Concat(lexeme.Display.Values.SelectMany(static form => form.Accepted))\n                    .Distinct(casing == \"lower-title-upper\"\n                        ? global::Humanizer.InflectionUnicodeData.SimpleCaseComparer.Instance\n                        : StringComparer.Ordinal);\n                if (forms.Skip(1).Any())\n                {\n                    throw new InvalidOperationException(\n                        $\"Invariant inflection lexeme '{lexeme.Id}' has divergent reachable forms.\");\n                }\n            }\n        }","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/InflectionCatalogValidation.cs#L146-L182","documentation":"An inflection owner with capability 'invariant' must not define any productive inflection rules. Invariant bundles represent nouns whose forms do not change across number/case, so rule-driven transformation is contradictory. The validator rejects any non-empty rules array on an invariant owner.","triggerScenarios":"An inflection owner YAML block has 'capability: invariant' and simultaneously has a non-empty 'rules:' list. The check at line 162 fires because rules.IsEmpty is false.","commonSituations":"Changing a locale's capability to 'invariant' without removing its previously productive rules. Copying a productive locale as a template for an invariant one and forgetting to strip the rules section.","solutions":["Remove all entries from the 'rules:' list (or remove the key entirely) for the invariant owner.","If the noun genuinely needs inflection, change 'capability' from 'invariant' to the appropriate productive value.","Review whether the owner should be split: invariant nouns in one owner, productive nouns in another."],"exampleFix":"# before\nowners:\n  - capability: 'invariant'\n    rules:\n      - id: 'plural-es'\n        output: '{stem}s'\n# after\nowners:\n  - capability: 'invariant'\n    rules: []","handlingStrategy":"validation","validationCode":"# Before building, verify invariant owners have no rules.\npython3 -c \"\nimport yaml, sys\nfor f in sys.argv[1:]:\n    d = yaml.safe_load(open(f))\n    owners = (d.get('inflection',{}).get('owners') or [])\n    for owner in owners:\n        if owner.get('capability') == 'invariant' and (owner.get('rules') or []):\n            print(f'{f}: invariant owner defines rules — remove them')\n\" src/Humanizer/Locales/*.yml","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When setting capability to 'invariant', immediately delete or empty the rules array.","Use a YAML template that has rules: [] commented out for invariant owners.","Understand that invariant means zero productive transformation — if rules are needed, the noun is not invariant."],"tags":["inflection","source-generator","invariant","rules","yaml","build-time"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}