{"record":{"id":"54123efcd0f117e3","repo":"AprilNEA/OpenLogi","slug":"resumable-fixture-cases-contain-unexpected-file-name","errorCode":null,"errorMessage":"resumable fixture cases contain unexpected file {name:?}","messagePattern":"resumable fixture cases contain unexpected file (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/openlogi-cli/src/cmd/fixture/contribute.rs","lineNumber":313,"sourceCode":"            None => bail!(\"in-progress contribution contains a non-UTF-8 entry\"),\n        }\n    }\n    Ok(())\n}\n\nfn validate_resumable_cases(directory: &Path, cassettes: &[HidCassette]) -> Result<()> {\n    let expected = cassettes\n        .iter()\n        .map(|cassette| format!(\"{}.json\", cassette.name))\n        .collect::<Vec<_>>();\n    for entry in fs::read_dir(directory).context(\"could not inspect resumable fixture cases\")? {\n        let entry = entry.context(\"could not inspect resumable fixture case\")?;\n        let name = entry.file_name();\n        let Some(name) = name.to_str() else {\n            bail!(\"resumable fixture cases contain a non-UTF-8 entry\");\n        };\n        if !expected.iter().any(|expected| expected == name) {\n            bail!(\"resumable fixture cases contain unexpected file {name:?}\");\n        }\n        require_regular_file(&entry.path(), \"fixture cassette\")?;\n    }\n    Ok(())\n}\n\nfn identity_plan(\n    profile: &DeviceProfile,\n    selected_route: &DeviceRoute,\n) -> Result<HidCassetteIdentityPlan> {\n    let mut plan = HidCassetteIdentityPlan::default();\n    let model = match selected_route {\n        DeviceRoute::Bolt { receiver_uid, slot } => {\n            plan.insert(\n                SanitizedIdentityKind::ReceiverUniqueId,\n                receiver_uid.as_bytes().to_vec(),\n            )?;\n            selected_model(profile, selected_route, *slot)?","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/AprilNEA/OpenLogi/blob/e846e6f4b4405e33bd6a9aaf949a482ce34cb6d8/crates/openlogi-cli/src/cmd/fixture/contribute.rs#L295-L331","documentation":"While validating a resumed contribution, every file found in the `cases/` directory must exactly match one of the cassette filenames (`<cassette-name>.json`) recorded in the resumable state. Any extra or differently-named entry causes this bail, preventing the CLI from publishing a fixture whose cassette set doesn't match the recorded state.","triggerScenarios":"Resuming `openlogi fixture contribute` when `<output>/cases/` contains a file not listed in the resumable state — e.g. leftover cassette JSON from a previous attempt with a different fixture id, editor backup files (`foo.json~`, `.DS_Store`), or a manually renamed/added cassette file.","commonSituations":"Reusing an output directory across contribution attempts with different `--id`; editors or OS tools leaving backup/metadata files in the cases directory; hand-renaming a cassette JSON to fix a typo; interrupted earlier run leaving stale cassettes.","solutions":["Inspect `<output>/cases/` and remove any file that isn't a cassette expected by the current contribution (backups, `.DS_Store`, stale JSON)","If the extra file belongs to the fixture, delete the resumable state file and restart the contribution so the state is regenerated consistently","Never manually add/rename cassette JSON files inside an in-progress contribution directory","Use a fresh output directory per contribution id"],"exampleFix":"// before: resumable cases dir polluted by an editor backup\n$ ls output/cases/\nmy-cassette.json  my-cassette.json~\n// after: remove the unexpected file and rerun\n$ rm output/cases/my-cassette.json~\n$ openlogi fixture contribute ...  # validation passes","handlingStrategy":"validation","validationCode":"use std::fs;\n\nfn cases_match_expected(cases_dir: &std::path::Path, expected: &[String]) -> std::io::Result<bool> {\n    let mut actual: Vec<String> = fs::read_dir(cases_dir)?\n        .filter_map(|e| e.ok())\n        .filter_map(|e| e.file_name().to_str().map(str::to_string))\n        .collect();\n    actual.sort();\n    let mut sorted = expected.to_vec();\n    sorted.sort();\n    Ok(actual == sorted)\n}","typeGuard":"fn is_expected_cassette(name: &str, expected: &[String]) -> bool {\n    expected.iter().any(|e| e == name)\n}","tryCatchPattern":null,"preventionTips":["Never reuse an output directory across contribution ids; one directory per fixture","Disable editor backup files in the fixture output directory (no `*~`, no `.DS_Store` — add it to editor ignore rules)","Delete the resumable state file rather than hand-editing cassettes when something goes wrong","Run `openlogi fixture verify` before resuming a contribution"],"tags":["filesystem","fixtures","cli","state-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"e846e6f4b4405e33bd6a9aaf949a482ce34cb6d8","analyzedAt":"2026-09-13T03:07:16.451Z","contentChangedAt":"2026-09-13T03:07:16.451Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}