{"record":{"id":"ed015c11819a38b2","repo":"Hmbown/CodeWhale","slug":"project-instruction-candidates-missing-required-entry","errorCode":null,"errorMessage":"PROJECT_INSTRUCTION_CANDIDATES missing required entry {candidate!r}","messagePattern":"PROJECT_INSTRUCTION_CANDIDATES missing required entry (.+?)","errorType":"exception","errorClass":"RuntimeContractError","httpStatus":null,"severity":"error","filePath":"scripts/check-runtime-contract-budget.py","lineNumber":573,"sourceCode":"    if \"load_project_instruction_fragment\" not in text:\n        raise RuntimeContractError(\n            \"bounded fragment module must expose load_project_instruction_fragment (project-instruction import as typed fragment)\"\n        )\n    if \"PROJECT_INSTRUCTION_CANDIDATES\" not in text:\n        raise RuntimeContractError(\n            \"bounded fragment module must define PROJECT_INSTRUCTION_CANDIDATES\"\n        )\n    # Required candidate files from #3978\n    required_candidates = [\n        \".cursorrules\",\n        \".clinerules\",\n        \".windsurf/rules\",\n        \".gemini\",\n        \".github/copilot-instructions.md\",\n    ]\n    for candidate in required_candidates:\n        if candidate not in text:\n            raise RuntimeContractError(\n                f\"PROJECT_INSTRUCTION_CANDIDATES missing required entry {candidate!r}\"\n            )\n\n    # matches_text recognizer must exist on the fragment trait\n    if \"fn matches_text\" not in text:\n        raise RuntimeContractError(\n            \"bounded fragment module must define a matches_text recognizer on the fragment trait\"\n        )\n    if \"trait ContextFragment\" not in text:\n        raise RuntimeContractError(\n            \"bounded fragment module must define trait ContextFragment with matches_text\"\n        )\n\n    # No unbounded fragment — enforce that creation clamps to MAX_FRAGMENT_BYTES\n    if \"MAX_FRAGMENT_BYTES\" not in text or \"enforce_byte_cap\" not in text:\n        raise RuntimeContractError(\n            \"bounded fragment module must enforce byte caps via enforce_byte_cap and MAX_FRAGMENT_BYTES\"\n        )","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/scripts/check-runtime-contract-budget.py#L555-L591","documentation":"After requiring PROJECT_INSTRUCTION_CANDIDATES to exist, check_fragment_caps scans fragments.rs for each required candidate file from issue #3978: .cursorrules, .clinerules, .windsurf/rules, .gemini, and .github/copilot-instructions.md. If any one literal is absent from the module text it names the missing entry. This keeps the typed project-instruction fragment covering the full set of supported editor instruction files.","triggerScenarios":"Running the contract check when PROJECT_INSTRUCTION_CANDIDATES exists in crates/core/src/fragments.rs but omits one or more of the five required candidate strings (trimmed list, renamed path, e.g. '.windsurfrules' instead of '.windsurf/rules').","commonSituations":"A developer trims the list to candidates their team actually uses; a candidate file is renamed upstream (e.g. .windsurf/rules layout change) and the entry is edited to match; the list is rewritten as a dynamic glob, dropping literal strings.","solutions":["Add the missing candidate literal (named in the error, e.g. '.gemini') back to PROJECT_INSTRUCTION_CANDIDATES in crates/core/src/fragments.rs.","If a candidate path legitimately changed, update both the list and this script's required_candidates in scripts/check-runtime-contract-budget.py together (documented change, not a silent edit).","Keep the list as literal string entries — the checker matches text, so computed/glob-based lists will fail."],"exampleFix":"// before\npub const PROJECT_INSTRUCTION_CANDIDATES: &[&str] = &[\".cursorrules\", \".clinerules\"];\n\n// after\npub const PROJECT_INSTRUCTION_CANDIDATES: &[&str] = &[\n    \".cursorrules\",\n    \".clinerules\",\n    \".windsurf/rules\",\n    \".gemini\",\n    \".github/copilot-instructions.md\",\n];","handlingStrategy":"validation","validationCode":"required = ['.cursorrules', '.clinerules', '.windsurf/rules', '.gemini', '.github/copilot-instructions.md']\ntext = open('crates/core/src/fragments.rs').read()\nmissing = [c for c in required if c not in text]\nassert not missing, f'missing candidates: {missing}'","typeGuard":"def candidates_complete(module_text: str) -> bool:\n    return all(c in module_text for c in ('.cursorrules', '.clinerules', '.windsurf/rules', '.gemini', '.github/copilot-instructions.md'))","tryCatchPattern":"try:\n    check_fragment_caps()\nexcept RuntimeContractError as e:\n    m = re.search(r\"missing required entry (.+)\", str(e))\n    if m:\n        append_candidate_to_list(m.group(1).strip(\"'\\\"\"))","preventionTips":["Never trim PROJECT_INSTRUCTION_CANDIDATES without updating required_candidates in the checker in the same commit.","Keep candidate entries as plain string literals — the checker is a text match, not an AST parse.","Add a unit-style smoke run of the script after any edit to the candidates list."],"tags":["rust","static-analysis","contract-check","fragments"],"backgroundTag":"schema-validation-failed","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}