{"record":{"id":"4fe3c5ad4b5206b7","repo":"opendataloader-project/opendataloader-pdf","slug":"no-skill-md-found-under-skill-dir-nothing-to-li","errorCode":null,"errorMessage":"no SKILL.md found under {skill_dir} (nothing to lint)","messagePattern":"no SKILL\\.md found under (.+?) \\(nothing to lint\\)","errorType":"validation","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"skills/odl-pdf-maintenance/sync-skill-refs.py","lineNumber":91,"sourceCode":"_FLAG_RE = re.compile(r\"(?<![A-Za-z0-9])--[a-z0-9][a-z0-9-]*\")\n# A run of dot-separated integers (semver OR IPv4); classified after matching.\n_DOTTED_RE = re.compile(r\"\\d+(?:\\.\\d+)+\")\n# Referenced bundle paths the prose points at (must exist under the skill dir).\n_PATH_RE = re.compile(r\"\\b((?:references|scripts)/[A-Za-z0-9_.-]+\\.(?:md|py|sh))\\b\")\n\n\nclass ConfigError(Exception):\n    \"\"\"Bad input/config (missing SKILL.md, unreadable file). Reported as exit 2,\n    distinct from exit 1 (a coupling/structural violation in the authored prose).\"\"\"\n\n\ndef _scanned_files(skill_dir: Path):\n    \"\"\"Agent-facing prose: SKILL.md + references/*.md. ConfigError if the dir does\n    not look like a skill bundle (no SKILL.md) — an empty/wrong --skill-dir must not\n    silently scan 0 files and report a false PASS.\"\"\"\n    skill_md = skill_dir / \"SKILL.md\"\n    if not skill_md.is_file():\n        raise ConfigError(f\"no SKILL.md found under {skill_dir} (nothing to lint)\")\n    files = [skill_md] + sorted((skill_dir / \"references\").glob(\"*.md\"))\n    return [f for f in files if f.is_file()]\n\n\ndef _read(f: Path) -> str:\n    try:\n        return f.read_text(encoding=\"utf-8\")\n    except OSError as e:\n        raise ConfigError(f\"{f}: {e}\") from e\n\n\ndef _is_ipv4(run: str) -> bool:\n    \"\"\"A dotted run that is a valid dotted-quad IPv4 (4 octets, each 0-255).\"\"\"\n    parts = run.split(\".\")\n    return len(parts) == 4 and all(p.isdigit() and 0 <= int(p) <= 255 for p in parts)\n\n\ndef check_versions(rel: str, text: str, violations: list):","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/opendataloader-project/opendataloader-pdf/blob/a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8/skills/odl-pdf-maintenance/sync-skill-refs.py#L73-L109","documentation":"ConfigError (exit code 2) raised by _scanned_files when the --skill-dir does not contain a SKILL.md file. The lint is designed to scan agent-facing prose (SKILL.md + references/*.md); without SKILL.md the directory is not a skill bundle, so scanning 0 files and reporting PASS would be a false negative. This input/config error is deliberately separated (exit 2) from a real coupling violation (exit 1).","triggerScenarios":"Running sync-skill-refs.py with a --skill-dir that points at the wrong directory (e.g. the repo root, the maintenance dir instead of the odl-pdf bundle dir, or a path where the skill has not been assembled).","commonSituations":"CI invokes the lint with a path that changed after a directory rename. A developer passes the maintenance dir (which contains the script, not SKILL.md). The skill bundle has not been generated/copied yet. A typo in the path.","solutions":["Point --skill-dir at the actual skill bundle directory containing SKILL.md (by default the script's sibling odl-pdf dir).","Verify with ls <skill-dir>/SKILL.md before running.","If the bundle is generated by a build step, ensure that step ran before the lint.","Distinguish exit code 2 (this config error) from exit 1 (a real violation) in CI scripting."],"exampleFix":"# before: wrong dir (the script's own dir, no SKILL.md)\n$ ./sync-skill-refs.py --skill-dir skills/odl-pdf-maintenance  # -> ConfigError\n# after: the distributed skill bundle\n$ ./sync-skill-refs.py --skill-dir skills/odl-pdf","handlingStrategy":"validation","validationCode":"from pathlib import Path\ndef is_skill_bundle(skill_dir: str) -> bool:\n    return (Path(skill_dir) / \"SKILL.md\").is_file()\n","typeGuard":"def is_no_skill_md_error(exc: Exception) -> bool:\n    return exc.__class__.__name__ == \"ConfigError\" and \"no SKILL.md found under\" in str(exc)","tryCatchPattern":"import subprocess, sys\nrc = subprocess.run([sys.executable, \"sync-skill-refs.py\", \"--skill-dir\", d]).returncode\nif rc == 2:\n    print(\"Config error (exit 2): bad --skill-dir; point at the dir containing SKILL.md\")\nelif rc == 1:\n    print(\"Lint violation (exit 1): fix the coupling issue in the prose\")","preventionTips":["Point --skill-dir at the bundle dir that contains SKILL.md (the sibling odl-pdf dir by default).","Distinguish exit code 2 (config) from exit 1 (real violation) in CI.","Verify ls <dir>/SKILL.md before running the lint."],"tags":["lint","skill","config","configerror","validation","python"],"backgroundTag":null,"analyzedSha":"a7789b8e77dd05e2b8659eb3ea12fc458f80bfb8","analyzedAt":"2026-08-14T05:22:03.953Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}