{
  "id": "overnight-agent-runs",
  "type": "loadout",
  "title": "Overnight agent runs with budgets and a morning report",
  "summary": "Queue coding tasks to run unattended, each with a finish-line check, its own worktree and branch, a dollar cap inside a nightly total, and safe retry settings.",
  "description": "Leaving agents running overnight is now common, and so are the two ways it goes wrong. One stuck task burns the whole budget. Or a rate or usage limit leaves a request waiting until morning with nothing done. Claude Code now has the controls for both. --max-budget-usd and --max-turns cap a print-mode run. CLAUDE_CODE_RETRY_WATCHDOG keeps retrying capacity errors, and since 2.1.295 and 2.1.301 it can be told to give up after a set wait or when a usage limit resets too far away.\n\nThis loadout turns those controls into a routine. Every task needs a finish line (a command that exits 0), and each runs in its own git worktree and branch. A standard-library runner gives each task the smaller of its budget and what is left of the night's total, sets the retry variables, runs the finish-line check after the agent, and writes a ledger and a morning report with result, cost, turns and commits per branch. It refuses --dangerously-skip-permissions outside a sandbox and never pushes or merges. The verifier drives it with a stub agent that prints Claude Code's documented JSON result.\n",
  "highlights": [
    "One finish-line command per task; no check, no unattended run",
    "Worktree and branch per task; nothing pushed, merged or deleted",
    "Per-task --max-budget-usd inside a nightly total; queue stops when it runs out",
    "Retry watchdog with MAX_WAIT_MS and MAX_USAGE_LIMIT_WAIT_MS, so limits end runs instead of hanging them",
    "Morning report per branch with result, cost, turns and commits"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "SetupBay",
    "url": "https://setupbay.com"
  },
  "credits": [
    {
      "title": "Claude Code docs: environment variables (CLAUDE_CODE_RETRY_WATCHDOG and its limits)",
      "url": "https://code.claude.com/docs/en/env-vars"
    },
    {
      "title": "Claude Code docs: CLI reference (--max-budget-usd, --max-turns, --worktree)",
      "url": "https://code.claude.com/docs/en/cli-reference"
    },
    {
      "title": "Claude Code changelog (2.1.239, 2.1.295, 2.1.301, 2.1.305 retry watchdog changes)",
      "url": "https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md"
    },
    {
      "title": "@harshit10sehgal: CLAUDE_CODE_RETRY_WATCHDOG for unattended runs",
      "url": "https://x.com/harshit10sehgal/status/2110984543494967721"
    },
    {
      "title": "@ekcheungAI: cap a headless run with --max-turns and --max-budget-usd",
      "url": "https://x.com/ekcheungAI/status/2111524159755010507"
    }
  ],
  "categories": [
    "workflow",
    "ai-agents"
  ],
  "stacks": [
    "python",
    "git",
    "bash"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "Rules for any unattended agent task.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "antigravity-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/AGENTS.md",
      "raw_latest_url": "https://setupbay.com/raw/overnight-agent-runs/latest/AGENTS.md",
      "bytes": 1307,
      "lang": "markdown",
      "content": "# AGENTS.md: unattended overnight agent runs\n\nAgents in this repo may run unattended (headless, overnight, in CI). Unattended work follows these rules, because\nnobody is there to stop a loop or answer a question.\n\n## Every unattended task\n- Has a **finish line**: one command that exits 0 when the task is done. Run it yourself before you stop.\n- Runs in its **own git worktree on its own branch** (`night/<date>-<task>`), never in the main working tree.\n- Has a **dollar cap** (`--max-budget-usd`) and a **turn cap** (`--max-turns`), inside a total for the night.\n- Commits its work to its branch, then ends with one line: done, or what is blocking.\n\n## Never, when unattended\n- Push, merge, open or close pull requests, delete branches, or touch other worktrees.\n- Edit, skip or delete tests to reach a finish line.\n- Change budgets, permissions, CI config or settings files.\n- Use secrets, production systems or paid services the task did not name.\n- Wait for a human: if a decision is needed, commit what you have and say what the decision is.\n\n## Running a batch\nUse the `night-shift` skill: it creates the worktrees, sets the budgets and Claude Code's unattended retry settings,\nruns each task, checks its finish line, and writes `.night-shift/report.md`. A human reviews every branch before merging.\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://setupbay.com/raw/overnight-agent-runs/latest/CLAUDE.md",
      "bytes": 392,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n\n- Skills from this loadout live in `.claude/skills/`: `night-shift`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From SetupBay \"Overnight agent runs with budgets and a morning report\" v1.0.0 — https://setupbay.com/loadouts/overnight-agent-runs/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/GEMINI.md",
      "raw_latest_url": "https://setupbay.com/raw/overnight-agent-runs/latest/GEMINI.md",
      "bytes": 393,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n\n- Skills from this loadout live in `.agents/skills/`: `night-shift`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From SetupBay \"Overnight agent runs with budgets and a morning report\" v1.0.0 — https://setupbay.com/loadouts/overnight-agent-runs/ -->\n"
    },
    {
      "path": "skills/night-shift/SKILL.md",
      "role": "skill",
      "description": "Plan, run and review an overnight batch.",
      "skill": "night-shift",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/night-shift/SKILL.md",
        "codex": ".agents/skills/night-shift/SKILL.md",
        "cursor": ".agents/skills/night-shift/SKILL.md",
        "gemini-cli": ".agents/skills/night-shift/SKILL.md",
        "antigravity-cli": ".agents/skills/night-shift/SKILL.md",
        "copilot": ".agents/skills/night-shift/SKILL.md"
      },
      "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/SKILL.md",
      "raw_latest_url": "https://setupbay.com/raw/overnight-agent-runs/latest/skills/night-shift/SKILL.md",
      "bytes": 3665,
      "lang": "markdown",
      "content": "---\nname: night-shift\ndescription: Queue coding tasks to run unattended overnight, each in its own git worktree with a dollar cap and a finish-line check, then read the morning report. Use when the user wants agents to work while they sleep, run a batch of tasks unattended, or asks what the overnight run did.\n---\n\n# Night shift\n\nUnattended runs fail in two expensive ways: one stuck task eats the whole budget, or a usage limit leaves a request\nhanging until morning with nothing to show. This skill gives every task a **finish line** (a command that exits 0\nwhen it is done), its own worktree and branch, its own dollar cap inside a total for the night, and Claude Code's\nunattended retry settings. Nothing is pushed or merged; the human reviews branches in the morning.\n\n## 1. Write the plan with the human\nCopy `references/night-shift.example.json` to `night-shift.json` at the repo root and fill it in together:\n- **One task = one finish line.** \"Make `pytest -q tests/test_dates.py` pass\" is a task. \"Improve the codebase\" is not.\n  If you cannot name the check command, the task is not ready for an unattended run.\n- `budget_usd` per task and `budget_usd_total` for the night. The script never lets a task start with less than\n  $0.50 left and passes Claude Code the smaller of the task budget and what is left as `--max-budget-usd`.\n- Permissions: headless runs cannot answer prompts, so anything not allowed is refused. Allow only what the tasks\n  need (`--allowedTools \"Bash(pytest *)\"`, `--permission-mode acceptEdits`). Never bypass permission checks\n  (bypassPermissions mode or the \"dangerously\" flag) outside a container or VM; the script refuses both unless\n  `NIGHT_SHIFT_SANDBOXED=1`.\n- Keep `--max-turns` (it ends a run that loops) and `--output-format json` (the script reads the cost from it).\n\n## 2. Start it\n```bash\npython3 .agents/skills/night-shift/scripts/night_shift.py run\n```\n(Claude Code installs the skill under `.claude/skills/`.) The main working tree must be committed first: each task\nbranches from `HEAD`. The script sets these unless they are already set:\n\n| Variable | Value | Why |\n|---|---|---|\n| `CLAUDE_CODE_RETRY_WATCHDOG` | `1` | keep retrying 429 and 529 errors instead of failing the task |\n| `CLAUDE_CODE_RETRY_WATCHDOG_MAX_WAIT_MS` | `1800000` | but end one request after 30 minutes of waiting (v2.1.295+) |\n| `CLAUDE_CODE_RETRY_WATCHDOG_MAX_USAGE_LIMIT_WAIT_MS` | `3600000` | end a request whose usage limit resets more than an hour away (v2.1.301+) |\n\nThe watchdog already fails at once on spend-limit and out-of-credits errors (v2.1.239+), so a hard limit stops the run.\nFor Codex, use `codex exec --json` as the agent and keep the budget in the spend-cap guard from the\n`agent-spend-cap` loadout; Codex has no budget flag.\n\n## 3. Read the morning report\n`.night-shift/report.md` has one row per task: finish line, result, cost, turns, commits, branch, and the agent's last\nline. `python3 .../night_shift.py report` rewrites it from `.night-shift/ledger.jsonl`. Then:\n- For a passed task: review the diff (`git log -p main..night/<date>-<task>`), run the full suite, and only then merge.\n  A passed finish line is not a review.\n- For a task that did not reach the finish line: read its last note and the commits. Make the task smaller or the\n  check clearer before queueing it again. Do not just raise the budget.\n- Remove finished worktrees: `git worktree remove .night-shift/worktrees/<task>`.\n\n## Never\n- Push, merge, open PRs or delete branches from an unattended run.\n- Queue tasks that need secrets, production access, or a human decision halfway through.\n- Edit the plan's budget upwards during the night.\n"
    },
    {
      "path": "skills/night-shift/scripts/night_shift.py",
      "role": "reference",
      "description": "Worktree-per-task runner with budgets, checks and a report.",
      "skill": "night-shift",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/night-shift/scripts/night_shift.py",
        "codex": ".agents/skills/night-shift/scripts/night_shift.py",
        "cursor": ".agents/skills/night-shift/scripts/night_shift.py",
        "gemini-cli": ".agents/skills/night-shift/scripts/night_shift.py",
        "antigravity-cli": ".agents/skills/night-shift/scripts/night_shift.py",
        "copilot": ".agents/skills/night-shift/scripts/night_shift.py"
      },
      "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/scripts/night_shift.py",
      "raw_latest_url": "https://setupbay.com/raw/overnight-agent-runs/latest/skills/night-shift/scripts/night_shift.py",
      "bytes": 8756,
      "lang": "markdown",
      "content": "#!/usr/bin/env python3\n\"\"\"Run a queue of agent tasks unattended: one git worktree and branch per task, a per-task and total\ndollar budget, Claude Code's unattended retry settings, a finish-line check, and a morning report.\n\nUsage:\n  python3 night_shift.py run [--plan night-shift.json]   run every task in the plan, in order\n  python3 night_shift.py report                           rewrite .night-shift/report.md from the ledger\n\nPlan (night-shift.json):\n  {\n    \"budget_usd_total\": 6.00,\n    \"agent\": [\"claude\", \"-p\", \"--output-format\", \"json\", \"--max-turns\", \"40\", \"--permission-mode\", \"acceptEdits\"],\n    \"tasks\": [ {\"id\": \"fix-flaky-date-test\", \"goal\": \"...\", \"check\": \"pytest -q tests/test_dates.py\", \"budget_usd\": 2.00} ]\n  }\nThe script appends --max-budget-usd (the smaller of the task budget and what is left) and the prompt.\nResults go to .night-shift/ledger.jsonl and .night-shift/report.md. Branches are named night/<date>-<id>.\nIt never pushes, merges or deletes a branch. Exit 0 when every task ran, 3 when the budget stopped the queue.\nStandard library only.\n\"\"\"\nimport datetime\nimport json\nimport os\nimport subprocess\nimport sys\n\nSTATE = \".night-shift\"\nWATCHDOG_DEFAULTS = {\n    \"CLAUDE_CODE_RETRY_WATCHDOG\": \"1\",                            # keep retrying 429/529 instead of failing at 3 a.m.\n    \"CLAUDE_CODE_RETRY_WATCHDOG_MAX_WAIT_MS\": \"1800000\",          # but give up on one request after 30 minutes of waits\n    \"CLAUDE_CODE_RETRY_WATCHDOG_MAX_USAGE_LIMIT_WAIT_MS\": \"3600000\",  # and end it if the usage limit resets more than 1 h away\n}\nMIN_TASK_USD = 0.50\n\n\ndef sh(cmd, cwd=None, timeout=None, env=None):\n    return subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout, env=env)\n\n\ndef die(msg, code=1):\n    print(f\"night-shift: {msg}\", file=sys.stderr)\n    sys.exit(code)\n\n\ndef load_plan(path):\n    try:\n        plan = json.load(open(path))\n    except (OSError, ValueError) as e:\n        die(f\"cannot read {path}: {e}\")\n    agent = plan.get(\"agent\") or []\n    if not agent:\n        die(\"plan has no agent command\")\n    bypass = any(a.startswith(\"--dangerously\") for a in agent) or \"bypassPermissions\" in agent\n    if bypass and os.environ.get(\"NIGHT_SHIFT_SANDBOXED\") != \"1\":\n        die(\"refusing to run with permission checks bypassed outside a sandbox (set NIGHT_SHIFT_SANDBOXED=1 only inside a container or VM)\")\n    if \"--max-budget-usd\" in agent:\n        die(\"leave --max-budget-usd out of the agent command; the script sets it per task from the budget\")\n    ids = [t.get(\"id\", \"\") for t in plan.get(\"tasks\", [])]\n    if not ids or any(not i or not i.replace(\"-\", \"\").isalnum() for i in ids) or len(set(ids)) != len(ids):\n        die(\"every task needs a unique id made of letters, digits and hyphens\")\n    for t in plan[\"tasks\"]:\n        if not t.get(\"goal\") or not t.get(\"check\"):\n            die(f\"task {t['id']} needs a goal and a check command (the finish line)\")\n    return plan\n\n\ndef spent_tonight(ledger, night):\n    return sum(r.get(\"cost_usd\", 0) for r in ledger if r.get(\"night\") == night)\n\n\ndef read_ledger():\n    p = os.path.join(STATE, \"ledger.jsonl\")\n    return [json.loads(l) for l in open(p)] if os.path.exists(p) else []\n\n\ndef prompt_for(task, branch):\n    return (\n        f\"{task['goal']}\\n\\n\"\n        f\"Finish line: `{task['check']}` exits 0 in this worktree. Run it yourself before you stop.\\n\"\n        f\"You are on branch {branch} in your own worktree. Commit your work to this branch with a clear message.\\n\"\n        \"Do not push, open pull requests, merge, or change files outside this worktree. Do not edit or skip tests to reach the finish line.\\n\"\n        \"If you cannot reach the finish line, commit what you have, then end with a short note: what is blocking, what you tried.\"\n    )\n\n\ndef run(plan_path):\n    plan = load_plan(plan_path)\n    if sh([\"git\", \"rev-parse\", \"--is-inside-work-tree\"]).returncode != 0:\n        die(\"run this from the root of a git repository\")\n    os.makedirs(os.path.join(STATE, \"worktrees\"), exist_ok=True)\n    exclude = sh([\"git\", \"rev-parse\", \"--git-path\", \"info/exclude\"]).stdout.strip()\n    if exclude and f\"/{STATE}/\" not in (open(exclude).read() if os.path.exists(exclude) else \"\"):\n        os.makedirs(os.path.dirname(exclude), exist_ok=True)\n        with open(exclude, \"a\") as fh:\n            fh.write(f\"\\n/{STATE}/\\n\")  # keep worktrees and the ledger out of git status\n    night = datetime.date.today().isoformat()\n    total = float(plan.get(\"budget_usd_total\", 0))\n    ledger_path = os.path.join(STATE, \"ledger.jsonl\")\n    stopped = False\n    env = dict(os.environ)\n    for k, v in WATCHDOG_DEFAULTS.items():\n        env.setdefault(k, v)\n    for task in plan[\"tasks\"]:\n        left = round(total - spent_tonight(read_ledger(), night), 4)\n        budget = round(min(float(task.get(\"budget_usd\", left)), left), 2)\n        if budget < MIN_TASK_USD:\n            print(f\"STOP before {task['id']}: ${left:.2f} left of ${total:.2f} tonight\")\n            stopped = True\n            break\n        branch = f\"night/{night}-{task['id']}\"\n        wt = os.path.join(STATE, \"worktrees\", task[\"id\"])\n        if os.path.exists(wt):\n            die(f\"{wt} already exists; review and remove it (git worktree remove) before re-running {task['id']}\")\n        r = sh([\"git\", \"worktree\", \"add\", \"-q\", \"-b\", branch, wt])\n        if r.returncode != 0:\n            die(f\"git worktree add failed for {task['id']}: {r.stderr.strip()}\")\n        cmd = [*plan[\"agent\"], \"--max-budget-usd\", f\"{budget:.2f}\", prompt_for(task, branch)]\n        print(f\"run {task['id']}: budget ${budget:.2f} (left ${left:.2f}), branch {branch}\")\n        started = datetime.datetime.now()\n        try:\n            a = sh(cmd, cwd=wt, timeout=int(task.get(\"timeout_min\", 90)) * 60, env=env)\n            out, code = a.stdout, a.returncode\n        except subprocess.TimeoutExpired:\n            out, code = \"\", \"timeout\"\n        try:\n            res = json.loads(out.strip().splitlines()[-1]) if out.strip() else {}\n        except ValueError:\n            res = {}\n        cost = float(res.get(\"total_cost_usd\") or 0)\n        chk = sh([\"bash\", \"-c\", task[\"check\"]], cwd=wt, timeout=1800)\n        ahead = sh([\"git\", \"rev-list\", \"--count\", f\"HEAD..{branch}\"]).stdout.strip() or \"0\"\n        row = {\n            \"night\": night, \"task\": task[\"id\"], \"branch\": branch, \"agent_exit\": code,\n            \"subtype\": res.get(\"subtype\", \"no-json\"), \"turns\": res.get(\"num_turns\"), \"cost_usd\": round(cost, 4),\n            \"check\": task[\"check\"], \"check_ok\": chk.returncode == 0, \"commits\": int(ahead),\n            \"minutes\": round((datetime.datetime.now() - started).total_seconds() / 60, 1),\n            \"note\": (res.get(\"result\") or \"\").strip().splitlines()[-1][:200] if res.get(\"result\") else \"\",\n        }\n        with open(ledger_path, \"a\") as fh:\n            fh.write(json.dumps(row) + \"\\n\")\n        print(f\"  {task['id']}: {'PASS' if row['check_ok'] else 'FAIL'} check, {row['subtype']}, ${cost:.4f}, {row['commits']} commit(s)\")\n    write_report()\n    return 3 if stopped else 0\n\n\ndef write_report():\n    ledger = read_ledger()\n    if not ledger:\n        die(\"no runs in the ledger yet\")\n    night = ledger[-1][\"night\"]\n    rows = [r for r in ledger if r[\"night\"] == night]\n    lines = [f\"# Night shift {night}\", \"\", \"| Task | Finish line | Result | Cost | Turns | Commits | Branch |\", \"|---|---|---|---|---|---|---|\"]\n    for r in rows:\n        result = \"passed\" if r[\"check_ok\"] else \"not reached\"\n        if r[\"subtype\"] not in (\"success\",):\n            result += f\" ({r['subtype']})\"\n        lines.append(f\"| {r['task']} | `{r['check']}` | {result} | ${r['cost_usd']:.2f} | {r['turns'] if r['turns'] is not None else '-'} | {r['commits']} | `{r['branch']}` |\")\n    spent = sum(r[\"cost_usd\"] for r in rows)\n    passed = sum(1 for r in rows if r[\"check_ok\"])\n    lines += [\"\", f\"Spent ${spent:.2f} on {len(rows)} task(s); {passed} reached the finish line.\", \"\"]\n    for r in rows:\n        if r[\"note\"]:\n            lines.append(f\"- {r['task']}: {r['note']}\")\n    lines += [\"\", \"Review each branch (`git log -p main..<branch>`) before merging. Nothing was pushed.\",\n              \"Remove a worktree when done with it: `git worktree remove .night-shift/worktrees/<task>`.\"]\n    with open(os.path.join(STATE, \"report.md\"), \"w\") as fh:\n        fh.write(\"\\n\".join(lines) + \"\\n\")\n    print(\"\\n\".join(lines[:4 + len(rows) + 2]))\n\n\ndef main(argv):\n    if not argv or argv[0] not in (\"run\", \"report\"):\n        print(__doc__.strip(), file=sys.stderr)\n        return 2\n    if argv[0] == \"report\":\n        write_report()\n        return 0\n    plan = argv[argv.index(\"--plan\") + 1] if \"--plan\" in argv else \"night-shift.json\"\n    return run(plan)\n\n\nif __name__ == \"__main__\":\n    sys.exit(main(sys.argv[1:]))\n"
    },
    {
      "path": "skills/night-shift/references/night-shift.example.json",
      "role": "reference",
      "description": "Example plan with three tasks.",
      "skill": "night-shift",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/night-shift/references/night-shift.example.json",
        "codex": ".agents/skills/night-shift/references/night-shift.example.json",
        "cursor": ".agents/skills/night-shift/references/night-shift.example.json",
        "gemini-cli": ".agents/skills/night-shift/references/night-shift.example.json",
        "antigravity-cli": ".agents/skills/night-shift/references/night-shift.example.json",
        "copilot": ".agents/skills/night-shift/references/night-shift.example.json"
      },
      "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/references/night-shift.example.json",
      "raw_latest_url": "https://setupbay.com/raw/overnight-agent-runs/latest/skills/night-shift/references/night-shift.example.json",
      "bytes": 936,
      "lang": "json",
      "content": "{\n  \"budget_usd_total\": 6.00,\n  \"agent\": [\"claude\", \"-p\", \"--output-format\", \"json\", \"--max-turns\", \"40\", \"--permission-mode\", \"acceptEdits\", \"--allowedTools\", \"Bash(pytest *)\", \"Bash(git add *)\", \"Bash(git commit *)\"],\n  \"tasks\": [\n    {\n      \"id\": \"fix-flaky-date-test\",\n      \"goal\": \"tests/test_dates.py::test_week_start fails on Sundays. Find the cause in src/dates.py and fix it.\",\n      \"check\": \"pytest -q tests/test_dates.py\",\n      \"budget_usd\": 2.00,\n      \"timeout_min\": 60\n    },\n    {\n      \"id\": \"csv-export-header\",\n      \"goal\": \"The CSV export in src/export.py has no header row. Add one, matching the column names in docs/export.md.\",\n      \"check\": \"pytest -q tests/test_export.py\",\n      \"budget_usd\": 1.50\n    },\n    {\n      \"id\": \"type-errors-billing\",\n      \"goal\": \"Make mypy pass for src/billing/ without adding type: ignore comments.\",\n      \"check\": \"mypy src/billing\",\n      \"budget_usd\": 2.50\n    }\n  ]\n}\n"
    }
  ],
  "requires": {
    "tools": [
      "python3",
      "git",
      "claude"
    ],
    "env": [],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "antigravity-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/2",
    "verdict": "clean",
    "network": true,
    "scripts": [
      "night_shift.py"
    ],
    "notes": "The runner starts the agent command you put in the plan (which calls its model provider) and runs your finish-line commands. It creates worktrees and branches under .night-shift/, adds that folder to .git/info/exclude, and never pushes, merges or deletes anything."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-24",
  "published": "2026-10-24",
  "updated": "2026-10-24",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "antigravity-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 1,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://setupbay.com/loadouts/overnight-agent-runs/",
  "api_url": "https://setupbay.com/api/loadouts/overnight-agent-runs.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-24T10:30:13Z",
    "commit": "42646ff9a52315018c9b0947c291c7ad980e1dc7",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 6 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 6 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 6 agents; layouts re-discovered",
        "details": [
          "Claude Code: 5 files, 1 skill in .claude/skills/",
          "Codex CLI: 4 files, 1 skill in .agents/skills/",
          "Cursor: 4 files, 1 skill in .agents/skills/",
          "Gemini CLI: 5 files, 1 skill in .agents/skills/",
          "Antigravity CLI: 4 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 4 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell, hidden text or covert instructions",
        "details": [
          "Scanner intake-precheck/2: verdict clean; network no (listing says yes)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "2 references between files resolve for every agent",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "6 files, 15.1 KB total; largest skills/night-shift/scripts/night_shift.py 8.6 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by SetupBay; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "The night-shift runner drives a stub Claude Code that prints the documented JSON result: one task fixes a bug on its own branch and passes its finish line, one hits its budget and is reported as not reached, and the third is refused because the night's total is spent. Each run got its own worktree, ",
        "details": [
          "Applied 9 loadout files at every agent's paths",
          "✓ set up a repo: bash -c cp -r project repo && cd repo && git init -q -b main && git config user.email fixture@example.com && git config user.name fixture && git add -A && git commit -qm base && echo ready",
          "✓ refuses a plan that bypasses permission checks: bash -c cd repo && sed 's/\"acceptEdits\"/\"bypassPermissions\"/' night-shift.json > /tmp/bad-plan.json && python3 ../.agents/skills/night-shift/scripts/night_shift.py run --plan /tmp/bad-plan.json; echo \"exit=$?\"",
          "✓ run the queue: bash -c cd repo && export STUB_LOG=$PWD/../stub.log PATH=\"$PWD/../bin:$PATH\" && python3 ../.agents/skills/night-shift/scripts/night_shift.py run; echo \"exit=$?\"",
          "✓ each run got its budget, the watchdog settings and its own branch: bash -c cat stub.log",
          "✓ morning report and a clean main tree: bash -c cd repo && cat .night-shift/report.md && git status --porcelain | wc -l && git log --oneline main | wc -l && git log --oneline main..night/$(date +%F)-fix-week-start && git remote | wc -l",
          "Covers: Per-task --max-budget-usd capped by what is left of the nightly total; the queue stops before a task that can't get $0.50",
          "Covers: CLAUDE_CODE_RETRY_WATCHDOG, _MAX_WAIT_MS and _MAX_USAGE_LIMIT_WAIT_MS set for every run",
          "Covers: Worktree and night/<date>-<task> branch per task; the main tree stays clean and nothing is pushed",
          "Covers: Claude Code's JSON result (subtype, num_turns, total_cost_usd) read into the ledger and report",
          "Covers: Not covered: a real Claude Code session (the stub prints the documented output shape)"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/night-shift/SKILL.md",
          "target": ".claude/skills/night-shift/SKILL.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/night-shift/scripts/night_shift.py",
          "target": ".claude/skills/night-shift/scripts/night_shift.py",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/scripts/night_shift.py",
          "role": "reference"
        },
        {
          "path": "skills/night-shift/references/night-shift.example.json",
          "target": ".claude/skills/night-shift/references/night-shift.example.json",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/references/night-shift.example.json",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/overnight-agent-runs/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/night-shift/SKILL.md",
          "target": ".agents/skills/night-shift/SKILL.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/night-shift/scripts/night_shift.py",
          "target": ".agents/skills/night-shift/scripts/night_shift.py",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/scripts/night_shift.py",
          "role": "reference"
        },
        {
          "path": "skills/night-shift/references/night-shift.example.json",
          "target": ".agents/skills/night-shift/references/night-shift.example.json",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/references/night-shift.example.json",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/overnight-agent-runs/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/night-shift/SKILL.md",
          "target": ".agents/skills/night-shift/SKILL.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/night-shift/scripts/night_shift.py",
          "target": ".agents/skills/night-shift/scripts/night_shift.py",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/scripts/night_shift.py",
          "role": "reference"
        },
        {
          "path": "skills/night-shift/references/night-shift.example.json",
          "target": ".agents/skills/night-shift/references/night-shift.example.json",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/references/night-shift.example.json",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/overnight-agent-runs/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/night-shift/SKILL.md",
          "target": ".agents/skills/night-shift/SKILL.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/night-shift/scripts/night_shift.py",
          "target": ".agents/skills/night-shift/scripts/night_shift.py",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/scripts/night_shift.py",
          "role": "reference"
        },
        {
          "path": "skills/night-shift/references/night-shift.example.json",
          "target": ".agents/skills/night-shift/references/night-shift.example.json",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/references/night-shift.example.json",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/overnight-agent-runs/gemini-cli.zip"
    },
    "antigravity-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/night-shift/SKILL.md",
          "target": ".agents/skills/night-shift/SKILL.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/night-shift/scripts/night_shift.py",
          "target": ".agents/skills/night-shift/scripts/night_shift.py",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/scripts/night_shift.py",
          "role": "reference"
        },
        {
          "path": "skills/night-shift/references/night-shift.example.json",
          "target": ".agents/skills/night-shift/references/night-shift.example.json",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/references/night-shift.example.json",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/overnight-agent-runs/antigravity-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/night-shift/SKILL.md",
          "target": ".agents/skills/night-shift/SKILL.md",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/night-shift/scripts/night_shift.py",
          "target": ".agents/skills/night-shift/scripts/night_shift.py",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/scripts/night_shift.py",
          "role": "reference"
        },
        {
          "path": "skills/night-shift/references/night-shift.example.json",
          "target": ".agents/skills/night-shift/references/night-shift.example.json",
          "raw_url": "https://setupbay.com/raw/overnight-agent-runs/1.0.0/skills/night-shift/references/night-shift.example.json",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/overnight-agent-runs/copilot.zip"
    }
  },
  "source_zip_url": "https://setupbay.com/download/overnight-agent-runs/source.zip"
}