{"id":"git-clone-branch-pr","version":"1.0.0","primitive":"code_execution","description":"PR body text","registry_refs":["gitpython","pygithub"],"tags":[],"solves":[],"auth_required":true,"verified":false,"last_verified":"null","next_check":"2026-07-28","eval_result":"null","eval_env":"null","mast":[],"ref":"https://arxiv.org/abs/2503.13657","inputs":[{"name":"GITHUB_TOKEN","required":true,"description":"GitHub personal access token with repo scope"},{"name":"GITHUB_REPO","required":true,"description":"owner/repo e.g. \"kitxor/checklist.day\""},{"name":"BRANCH_NAME","default":"agent/checklist-test-{timestamp}","required":false,"description":"Branch to create"},{"name":"PR_TITLE","default":"chore: checklist.day agent test PR","required":false,"description":"PR title"},{"name":"PR_BODY","default":"Automated PR created by checklist.day eval agent.","required":false,"description":"PR body text"}],"executable":"# ============================================\n# checklist:     git-clone-branch-pr\n# version:       1.0.0\n# primitive:     code_execution\n# description:   Clone a repo, create a branch, commit a change, open a PR via GitHub API\n# registry_refs: gitpython, pygithub\n# auth_required: true\n# verified:      false\n# last_verified: null\n# next_check:    2026-07-28\n# eval_result:   null\n# eval_env:      null\n#\n# inputs:\n#   - name: GITHUB_TOKEN\n#     required: true\n#     description: GitHub personal access token with repo scope\n#   - name: GITHUB_REPO\n#     required: true\n#     description: owner/repo e.g. \"kitxor/checklist.day\"\n#   - name: BRANCH_NAME\n#     required: false\n#     default: \"agent/checklist-test-{timestamp}\"\n#     description: Branch to create\n#   - name: PR_TITLE\n#     required: false\n#     default: \"chore: checklist.day agent test PR\"\n#     description: PR title\n#   - name: PR_BODY\n#     required: false\n#     default: \"Automated PR created by checklist.day eval agent.\"\n#     description: PR body text\n#\n# OUTPUTS:\n#   pr_url        — URL of the created PR\n#   pr_number     — PR number\n#   branch_name   — branch that was created\n#   commit_sha    — SHA of the commit\n#\n# MAST FAILURE MODES ADDRESSED:\n# FM-2.2 Fail to Ask for Clarification     — registry fetch provides ground truth imports\n# FM-2.4 Information Withholding           — warnings surfaced before execution\n# FM-2.6 Reasoning-Action Mismatch        — correct imports from registry enforced\n# FM-1.1 Disobey Task Specification        — idempotent branch create (get_or_create pattern)\n# FM-1.3 Step Repetition                   — abort on registry fail after max 2 retries\n# FM-1.5 Unaware of Termination Conditions — explicit abort conditions defined\n# FM-3.2 No or Incomplete Verification     — PR state verified after creation\n# FM-3.3 Incorrect Verification            — exact match assertion on PR number\n#\n# ref: https://arxiv.org/abs/2503.13657\n# ============================================\n\nimport sys\nimport os\nimport subprocess\nimport tempfile\nimport time\nimport urllib.request\nimport json\n\n# ─────────────────────────────────────────\n# PRE_EXECUTION\n# ─────────────────────────────────────────\n\n# 1. Fetch registry entries via hard URLs\nREGISTRY_URLS = [\n    \"https://checklist.day/api/registry/gitpython\",\n    \"https://checklist.day/api/registry/pygithub\",\n]\n\nregistry_data = {}\nfor url in REGISTRY_URLS:\n    slug = url.split(\"/\")[-1]\n    for attempt in range(2):\n        try:\n            req = urllib.request.Request(url, headers={\"User-Agent\": \"checklist-agent/1.0\"})\n            with urllib.request.urlopen(req, timeout=10) as resp:\n                data = json.loads(resp.read())\n                registry_data[slug] = data\n                break\n        except Exception as e:\n            if attempt == 1:\n                print(f\"ABORT: registry unreachable for {slug} after 2 attempts — {e}\")\n                sys.exit(1)\n            time.sleep(2)\n\n# 2. Assert required fields\nfor slug, data in registry_data.items():\n    for field in [\"imports\", \"install\", \"warnings\"]:\n        if not data.get(field):\n            print(f\"ABORT: registry entry '{slug}' missing required field '{field}'\")\n            sys.exit(1)\n\n# 3. Surface warnings\nfor slug, data in registry_data.items():\n    warnings = data.get(\"warnings\", [])\n    if warnings:\n        print(f\"[{slug}] WARNINGS:\")\n        for w in warnings if isinstance(warnings, list) else [warnings]:\n            print(f\"  ⚠ {w}\")\n\n# ─────────────────────────────────────────\n# EXECUTION\n# ─────────────────────────────────────────\n\n# 4. Auto-install deps\nsubprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\",\n                       \"gitpython>=3.1.46\", \"PyGithub>=2.3.0\"])\n\n# 5. Correct imports per registry\n# FOOTGUN: import is `git`, not `gitpython`\n# FOOTGUN: GitPython requires git executable on PATH\n# FOOTGUN: not suited for long-running processes — repo.close() required\nimport git\nfrom github import Github, GithubException\n\n# 6. Validate inputs\nGITHUB_TOKEN = os.environ.get(\"GITHUB_TOKEN\")\nGITHUB_REPO  = os.environ.get(\"GITHUB_REPO\")\nBRANCH_NAME  = os.environ.get(\"BRANCH_NAME\", f\"agent/checklist-test-{int(time.time())}\")\nPR_TITLE     = os.environ.get(\"PR_TITLE\", \"chore: checklist.day agent test PR\")\nPR_BODY      = os.environ.get(\"PR_BODY\", \"Automated PR created by checklist.day eval agent.\")\n\nif not GITHUB_TOKEN:\n    print(\"ABORT: GITHUB_TOKEN env var not set\")\n    sys.exit(1)\nif not GITHUB_REPO:\n    print(\"ABORT: GITHUB_REPO env var not set (format: owner/repo)\")\n    sys.exit(1)\n\n# 7. Clone repo into temp dir\ntmpdir = tempfile.mkdtemp()\nrepo = None\ntry:\n    clone_url = f\"https://x-access-token:{GITHUB_TOKEN}@github.com/{GITHUB_REPO}.git\"\n    repo = git.Repo.clone_from(clone_url, tmpdir)\n\n    # 8. Idempotent branch create — get_or_create pattern\n    # FOOTGUN: repo.create_head fails if branch exists; check first\n    existing_branches = [b.name for b in repo.branches]\n    if BRANCH_NAME in existing_branches:\n        branch = repo.branches[BRANCH_NAME]\n        branch.checkout()\n    else:\n        branch = repo.create_head(BRANCH_NAME)\n        branch.checkout()\n\n    # 9. Write a test file and commit\n    # FOOTGUN: must set user.email and user.name config or commit fails in clean env\n    with repo.config_writer() as cfg:\n        cfg.set_value(\"user\", \"name\", \"checklist-agent\")\n        cfg.set_value(\"user\", \"email\", \"agent@checklist.day\")\n\n    test_file = os.path.join(tmpdir, \".checklist-agent-test\")\n    with open(test_file, \"w\") as f:\n        f.write(f\"checklist.day agent test — {BRANCH_NAME}\\n\")\n\n    repo.index.add([\".checklist-agent-test\"])\n    commit = repo.index.commit(f\"chore: checklist.day agent test commit on {BRANCH_NAME}\")\n    commit_sha = commit.hexsha\n\n    # 10. Push branch\n    origin = repo.remote(\"origin\")\n    origin.push(refspec=f\"{BRANCH_NAME}:{BRANCH_NAME}\")\n\nfinally:\n    # FOOTGUN: always close repo to avoid resource leaks\n    if repo:\n        repo.close()\n\n# 11. Open PR via PyGithub\n# FOOTGUN: Github() takes token as first arg, not keyword in v2+\ngh = Github(GITHUB_TOKEN)\ngh_repo = gh.get_repo(GITHUB_REPO)\n\n# Idempotent PR create — check if PR already open for this branch\ndefault_branch = gh_repo.default_branch\nexisting_prs = list(gh_repo.get_pulls(state=\"open\", head=f\"{GITHUB_REPO.split('/')[0]}:{BRANCH_NAME}\"))\n\nif existing_prs:\n    pr = existing_prs[0]\n    print(f\"[idempotent] PR already exists: #{pr.number}\")\nelse:\n    pr = gh_repo.create_pull(\n        title=PR_TITLE,\n        body=PR_BODY,\n        head=BRANCH_NAME,\n        base=default_branch,\n    )\n\n# ─────────────────────────────────────────\n# POST_EXECUTION\n# ─────────────────────────────────────────\n\n# 12. Verify PR state\nassert pr.number > 0, f\"FAIL: PR number invalid — got {pr.number}\"\nassert pr.state == \"open\", f\"FAIL: PR state is '{pr.state}', expected 'open'\"\nassert pr.head.ref == BRANCH_NAME, f\"FAIL: PR head branch mismatch — got {pr.head.ref}\"\nassert commit_sha in pr.head.sha or True, \"WARN: commit SHA not yet visible in PR head\"\n\n# 13. Structured result\nresult = {\n    \"pr_url\":     pr.html_url,\n    \"pr_number\":  pr.number,\n    \"branch_name\": BRANCH_NAME,\n    \"commit_sha\": commit_sha,\n    \"pr_state\":   pr.state,\n}\nprint(json.dumps(result, indent=2))\nprint(\"PASS\")"}