{"record":{"id":"75b3d76f024c1787","repo":"headroomlabs-ai/headroom","slug":"expected-one-ruff-dev-dependency-found-ruff-requ","errorCode":null,"errorMessage":"expected one Ruff dev dependency, found {ruff_requirements!r}","messagePattern":"expected one Ruff dev dependency, found (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scripts/verify-ruff-version.py","lineNumber":33,"sourceCode":"\nROOT = Path(__file__).resolve().parent.parent\nRUFF_PRE_COMMIT_REPO = \"https://github.com/astral-sh/ruff-pre-commit\"\nWORKFLOW_VERSION_COMMAND = \"python scripts/verify-ruff-version.py --print-version\"\nWORKFLOW_INSTALL_REFERENCE = \"steps.ruff-version.outputs.version\"\n\n\ndef _load_toml(path: Path) -> dict[str, Any]:\n    with path.open(\"rb\") as file:\n        return cast(dict[str, Any], tomllib.load(file))\n\n\ndef _authoritative_version() -> str:\n    dependencies = _load_toml(ROOT / \"pyproject.toml\")[\"project\"][\"optional-dependencies\"][\"dev\"]\n    ruff_requirements = [\n        requirement for requirement in dependencies if requirement.startswith(\"ruff\")\n    ]\n    if len(ruff_requirements) != 1:\n        raise ValueError(f\"expected one Ruff dev dependency, found {ruff_requirements!r}\")\n\n    match = re.fullmatch(r\"ruff==([^;,\\s]+)\", ruff_requirements[0])\n    if match is None:\n        raise ValueError(\n            \"pyproject.toml must contain one exact Ruff pin in project.optional-dependencies.dev\"\n        )\n    return match.group(1)\n\n\ndef _locked_version() -> str:\n    packages = _load_toml(ROOT / \"uv.lock\")[\"package\"]\n    versions = [str(package[\"version\"]) for package in packages if package[\"name\"] == \"ruff\"]\n    if len(versions) != 1:\n        raise ValueError(f\"expected one locked Ruff package, found {versions!r}\")\n    return versions[0]\n\n\ndef _pre_commit_version() -> str:","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/scripts/verify-ruff-version.py#L15-L51","documentation":"verify-ruff-version.py reads `project.optional-dependencies.dev` from pyproject.toml and requires exactly one requirement starting with `ruff`. Zero entries (Ruff dependency removed) or two-plus (e.g., both `ruff` and a `ruff-*` plugin pinned) fail, because the script cannot tell which one is the authoritative version pin.","triggerScenarios":"Adding a second Ruff-family dev dependency (any requirement string beginning with `ruff`, such as `ruff-vscode` or a duplicated pin); removing the Ruff pin entirely; moving Ruff to a different dependency group.","commonSituations":"Dependabot/Renovate PRs that add or duplicate Ruff entries; refactors moving dev deps into separate groups; a rename that accidentally starts with the substring `ruff`.","solutions":["Inspect pyproject.toml's dev group: `grep -n ruff pyproject.toml` and keep exactly one entry.","Remove the duplicate, or rename the non-tool dependency so it no longer starts with `ruff`.","If Ruff moved groups on purpose, update the script's lookup path to the new group."],"exampleFix":"# pyproject.toml - before\n[project.optional-dependencies]\ndev = [\"ruff==0.5.0\", \"ruff-lsp==0.1.0\"]\n\n# after\n[project.optional-dependencies]\ndev = [\"ruff==0.5.0\"]","handlingStrategy":"validation","validationCode":"import tomllib\nfrom pathlib import Path\n\ndef ruff_pins(pyproject: Path) -> list[str]:\n    deps = tomllib.loads(pyproject.read_text())[\"project\"][\"optional-dependencies\"][\"dev\"]\n    return [d for d in deps if d.startswith(\"ruff\")]\n# assert len(ruff_pins(...)) == 1 in a pre-commit hook","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep exactly one Ruff entry in the dev group; name any unrelated package so it does not start with 'ruff'.","Review dependency-bot PRs for duplicated Ruff requirements.","Run scripts/verify-ruff-version.py in CI so drift is caught at PR time."],"tags":["ruff","pyproject","dependencies","linting"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}