{"record":{"id":"06fd2762cfbae1d9","repo":"abhigyanpatwari/GitNexus","slug":"skill-fingerprint-input-must-be-a-regular-non-syml","errorCode":null,"errorMessage":"skill fingerprint input must be a regular non-symlink file: {path}","messagePattern":"skill fingerprint input must be a regular non-symlink file: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/evolution.py","lineNumber":470,"sourceCode":"    for skill_name in skill_names:\n        _require_directory_chain(\n            worktree,\n            Path(\".claude\") / \"skills\" / skill_name,\n            label=\"skill fingerprint root\",\n        )\n\n    entries = sorted(\n        (path for skill_name in skill_names for path in (worktree / \".claude\" / \"skills\" / skill_name).rglob(\"*\")),\n        key=lambda path: path.relative_to(worktree).as_posix(),\n    )\n    files: list[Path] = []\n    total = 0\n    for path in entries:\n        metadata = path.lstat()\n        if stat.S_ISDIR(metadata.st_mode):\n            continue\n        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode):\n            raise ValueError(f\"skill fingerprint input must be a regular non-symlink file: {path}\")\n        total += metadata.st_size\n        if total > MAX_SKILL_FINGERPRINT_BYTES:\n            raise ValueError(\"skill fingerprint input exceeds the bounded evidence limit\")\n        files.append(path)\n    return fingerprint_files(worktree, files)\n\n\ndef evaluate_candidate(\n    results: dict[str, dict[str, dict[str, Any]]],\n    *,\n    incumbent_arm: str,\n    candidate_arm: str,\n    model: str | None,\n    metric: str = \"cost_usd\",\n    min_runs: int = 3,\n    min_improvement_pct: float = 5.0,\n    max_task_regression_pct: float = 20.0,\n) -> dict[str, Any]:","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/evolution.py#L452-L488","documentation":"skill_fingerprint walks `.claude/skills/<skill>` for the arm's skills and refuses any entry that is neither a directory nor a plain regular non-symlink file. lstat (not stat) is used so symlinks are detected as symlinks rather than followed. The guard keeps the evidence that feeds the promotion gate deterministic and prevents a crafted skill tree from redirecting the read.","triggerScenarios":"A skill directory contains a symlink (e.g. a shared snippet `ln -s ../../shared/rules.md`), a broken symlink, or a special file (fifo/socket/device) created by accident or by a packaging step.","commonSituations":"Cross-skill shared content introduced via symlinks during local authoring; a vendored grammar or fixture symlinked into a skill dir; broken symlinks left after a move.","solutions":["Find offending entries: `find .claude/skills/<skill> -type l -o -type p -o -type s`.","Replace symlinks with real copies: `cp -L --remove-destination <link> <link>`.","Regenerate the skill packaging so it ships only regular files."],"exampleFix":"# before\nln -s ../../shared/rules.md .claude/skills/gitnexus-plan/rules.md\n\n# after\ncp ../../shared/rules.md .claude/skills/gitnexus-plan/rules.md","handlingStrategy":"validation","validationCode":"import stat, os\nfrom pathlib import Path\n\ndef skill_files_are_regular(root: Path, skill_names: list[str]) -> list[Path]:\n    out = []\n    for name in skill_names:\n        for p in (root / \".claude\" / \"skills\" / name).rglob(\"*\"):\n            m = p.lstat().st_mode\n            if stat.S_ISDIR(m):\n                continue\n            if stat.S_ISLNK(m) or not stat.S_ISREG(m):\n                raise ValueError(f\"non-regular skill file: {p}\")\n            out.append(p)\n    return out\n\nskill_files_are_regular(clone, skill_names)","typeGuard":"import stat\nfrom pathlib import Path\n\ndef is_regular_non_symlink(path: Path) -> bool:\n    m = path.lstat().st_mode\n    return stat.S_ISREG(m) and not stat.S_ISLNK(m)","tryCatchPattern":null,"preventionTips":["Author skills with copies, never symlinks: `cp` not `ln -s`.","Audit shipped skill trees in CI: `find .claude/skills -type l` must be empty.","Reject special files at packaging time so they never reach the fingerprint step."],"tags":["workflow-bench","fingerprint","filesystem","validation","symlink"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}