{"record":{"id":"fda3c296f4230abd","repo":"abhigyanpatwari/GitNexus","slug":"planning-deleted-existing-plan-artifact-s-delet","errorCode":null,"errorMessage":"planning deleted existing plan artifact(s): {deleted}","messagePattern":"planning deleted existing plan artifact\\(s\\): (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/runner_artifacts.py","lineNumber":293,"sourceCode":"            if not stat.S_ISREG(opened.st_mode) or opened.st_dev != metadata.st_dev or opened.st_ino != metadata.st_ino:\n                raise ValueError(f\"plan artifact changed while opening: {path}\")\n            with os.fdopen(descriptor, \"rb\", closefd=False) as handle:\n                snapshot[path] = hashlib.file_digest(handle, \"sha256\").hexdigest()\n            after = os.fstat(descriptor)\n            if (opened.st_size, opened.st_mtime_ns) != (after.st_size, after.st_mtime_ns):\n                raise ValueError(f\"plan artifact changed while hashing: {path}\")\n        finally:\n            os.close(descriptor)\n    return snapshot\n\n\ndef new_plan_doc(worktree: Path, before: dict[Path, str]) -> Path:\n    \"\"\"Return the sole new or modified plan, rejecting ambiguous evidence.\"\"\"\n\n    after = snapshot_plan_docs(worktree)\n    deleted = sorted(path for path in before if path not in after)\n    if deleted:\n        raise ValueError(\"planning deleted existing plan artifact(s): \" + \", \".join(str(path) for path in deleted))\n    changed = sorted(path for path, digest in after.items() if before.get(path) != digest)\n    if len(changed) != 1:\n        raise ValueError(f\"planning must create or modify exactly one plan artifact; observed {len(changed)}\")\n    return changed[0]\n\n\ndef make_worktree(repo: Path, ref: str, parent: Path) -> Path:\n    \"\"\"Create a self-contained clone per benchmark arm.\"\"\"\n\n    target = Path(tempfile.mkdtemp(prefix=\"wfbench-\", dir=parent))\n    target.rmdir()\n    try:\n        run_checked(\n            [\n                \"git\",\n                \"clone\",\n                \"--no-local\",\n                \"--no-hardlinks\",","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/runner_artifacts.py#L275-L311","documentation":"Thrown by new_plan_doc when comparing the before-snapshot to the after-snapshot of plan artifacts. If any path present in the before-dict is absent from the after-dict, the planning phase deleted a previously existing plan markdown/html file. The benchmark requires planning to be additive, so destructive behavior is rejected as ambiguous evidence.","triggerScenarios":"In new_plan_doc, `deleted = sorted(path for path in before if path not in after)` is non-empty — a plan file that existed before the planning phase no longer exists after it.","commonSituations":"An agent removes a stale plan before writing its own; a git checkout/clean inside the agent step deletes docs/plans; a setup hook that tidies the plans dir; the agent treats docs/plans as scratch space.","solutions":["Make the planning step only create or modify plans, never delete existing ones.","If a stale plan must be replaced, overwrite it in place rather than unlink + create.","Audit agent prompts/tooling for any `rm`/`git clean` touching docs/plans.","Pre-populate the worktree with no pre-existing plans if the task does not require editing one, so there is nothing to delete."],"exampleFix":"// before — agent deletes then writes\nos.remove(plan_path)\nplan_path.write_text(new_plan)\n\n// after — overwrite in place, never delete\nplan_path.write_text(new_plan)","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef plans_only_added(before: dict[Path, str], after: dict[Path, str]) -> bool:\n    return all(p in after for p in before)","typeGuard":"null","tryCatchPattern":"try:\n    new_plan_doc(worktree, before)\nexcept ValueError as e:\n    if 'deleted existing plan artifact' in str(e):\n        # planning was destructive; fix the agent/prompt to overwrite, not delete\n        raise\n    raise","preventionTips":["Instruct planning steps to create or overwrite plans, never unlink existing ones.","Pre-populate the worktree with no plans if none need editing.","Audit agent tool output for `rm`/`git clean` against docs/plans.","Add a unit test asserting plans_only_added(before, after) before calling new_plan_doc."],"tags":["plan-artifacts","integrity","workflow-bench","validation"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}