{"record":{"id":"3c7a561b5eba6b91","repo":"affaan-m/ECC","slug":"spec-file-not-found-path","errorCode":null,"errorMessage":"Spec file not found: {path}","messagePattern":"Spec file not found: (.+?)","errorType":"exception","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"skills/skill-comply/scripts/parser.py","lineNumber":80,"sourceCode":"        try:\n            events.append(ObservationEvent(\n                timestamp=raw[\"timestamp\"],\n                event=raw[\"event\"],\n                tool=raw[\"tool\"],\n                session=raw[\"session\"],\n                input=raw.get(\"input\", \"\"),\n                output=raw.get(\"output\", \"\"),\n            ))\n        except KeyError as e:\n            raise ValueError(f\"Missing required field {e} at line {i}\") from e\n\n    return sorted(events, key=lambda e: e.timestamp)\n\n\ndef parse_spec(path: Path) -> ComplianceSpec:\n    \"\"\"Parse a YAML compliance spec file.\"\"\"\n    if not path.is_file():\n        raise FileNotFoundError(f\"Spec file not found: {path}\")\n    raw = yaml.safe_load(path.read_text())\n\n    steps: list[Step] = []\n    for s in raw[\"steps\"]:\n        d = s[\"detector\"]\n        steps.append(Step(\n            id=s[\"id\"],\n            description=s[\"description\"],\n            required=s[\"required\"],\n            detector=Detector(\n                description=d[\"description\"],\n                after_step=d.get(\"after_step\"),\n                before_step=d.get(\"before_step\"),\n            ),\n        ))\n\n    if \"scoring\" not in raw:\n        raise KeyError(\"Missing 'scoring' section in compliance spec\")","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/skills/skill-comply/scripts/parser.py#L62-L98","documentation":"Raised by parse_spec() in skill-comply when the supplied YAML compliance-spec path does not resolve to an existing regular file. The guard is path.is_file() at the top of the function, identical in shape to parse_trace's file check.","triggerScenarios":"Passing a stale spec path; a Path constructed from a config value that points at the wrong directory; an unexpanded '~'; pointing at a directory instead of a .yaml file.","commonSituations":"generate_spec wrote the spec to a tempfile that was already unlinked; CI checkout missing the specs/ directory; a CLI flag that takes the spec path was left at its default.","solutions":["Confirm path.is_file() at the call site and log path.resolve().","Use the canonical specs/ directory under the skill-comply package, derived from __file__ rather than hardcoded strings.","Expand user paths with Path.expanduser().","If the spec was generated, ensure spec_generator.generate_spec completed and its tempfile was not deleted before parse_spec ran."],"exampleFix":"# before\nspec = parse_spec(Path('specs/skill.yaml'))\n\n# after\nfrom pathlib import Path\nspec_path = (Path(__file__).parent / 'specs' / 'skill.yaml').resolve()\nif not spec_path.is_file():\n    raise SystemExit(f'spec missing: {spec_path}')\nspec = parse_spec(spec_path)","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef safe_parse_spec(path: Path):\n    resolved = Path(path).expanduser().resolve()\n    if not resolved.is_file():\n        raise SystemExit(f'spec not found: {resolved}')\n    from scripts.parser import parse_spec\n    return parse_spec(resolved)","typeGuard":null,"tryCatchPattern":"from scripts.parser import parse_spec\ntry:\n    spec = parse_spec(path)\nexcept FileNotFoundError as e:\n    raise SystemExit(f'compliance spec missing: {e}') from e","preventionTips":["Locate specs via package-relative paths: Path(__file__).parent / 'specs' / name.","If generate_spec is the producer, do not unlink its tempfile until parse_spec has finished.","Treat an absent spec as a build error, not a runtime error — fail fast in CI."],"tags":["file-io","skill-comply","spec","yaml","validation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}