Ai-Mee Help Centre
Home
Features
How-To Guides
FAQ
Need Help?
Home
Features
How-To Guides
FAQ
Need Help?

Cloud agent — autonomous issue → PR

Run Claude on a GitHub issue unattended and remotely, iterating until done, using Claude Code on the web (Anthropic-managed cloud sessions) instead of GitHub Actions.

Why this over GitHub Actions: the agent loop runs on Anthropic's VM and bills to the Max subscription's rate limits — 0 GitHub Actions minutes. Since 1 March 2026 self-hosted runners are metered on private repos, so the old @claude workflow (.github/workflows/claude.yml) is no longer free. That workflow is kept as a fallback but should be retired once this is trusted.

Flow

issue labelled `claude`
  → Routine fires a cloud session
  → session clones the repo (.claude/skills/start, CLAUDE.md, hooks all load)
  → setup script (cached, repo-independent): supabase CLI + Playwright browser
  → SessionStart hook (scripts/cloud-session-start.sh):
        pnpm install → start dockerd (overlay2, iptables off)
        → supabase start -x edge-runtime && db reset (retried once, clean slate)
        → write front-end/.env.local + api/.env (only if absent)
        → symlink /opt/google/chrome/chrome → the sandbox Chromium
        (all best-effort; writes /tmp/cloud-supabase-status = ready|failed)
  → prompt: /start <issue>   → implements; verifies with live /verify-fe
                               (Supabase is normally `ready`), else mock-mode;
                               opens a PR via the GitHub MCP tools (Closes #<n>)
  → Auto-fix watches ci.yml and pushes fixes until green — no human @claude

Sandbox reality (verified in live sessions):

  • No gh CLI — GitHub goes through the built-in mcp__github__* tools + the git push proxy (scoped to the current branch).
  • Docker daemon isn't running by default but the hook starts it. The sandbox kernel is 6.18.x, so --storage-driver=overlay2 (dedupes image layers — vfs exhausts the 30 GB disk pulling ~12 Supabase images) with --iptables=false --ip6tables=false (port publishing still works via the userland docker-proxy). Background: anthropics/claude-code#29515.
  • Local Supabase comes up — the hook runs supabase start -x edge-runtime and writes ready to /tmp/cloud-supabase-status, so /start uses the live /verify-fe path. The one excluded service is edge-runtime: the sandbox strips CAP_SYS_RESOURCE, so runc can't raise its nofile rlimit and it fails to start (aborting the whole stack unless excluded). Edge Functions therefore aren't testable in-sandbox; every other service (db, auth, rest, storage, studio, realtime, kong) is healthy. If Docker/Supabase ever fail, the hook records failed and verification falls back to ci.yml's mock-mode suite (unit + route + Playwright e2e with fake VITE_SUPABASE_*).

One-time setup

1. Install the Claude GitHub App (required for Auto-fix)

Onboard at claude.ai/code using the GitHub App method (not just /web-setup). The App delivers the PR webhooks that Auto-fix needs. Install it on the ai-marketing repo. This also provides the built-in mcp__github__* tools and the git push proxy the session uses — no gh CLI or GH_TOKEN required.

2. Create a cloud environment + setup script

In the claude.ai web UI, add an environment (cloud icon → Add environment). The setup script runs once and its filesystem is cached (snapshotted), so keep it under ~5 minutes.

The setup script does NOT run inside your repo. Its working directory is /home/user and the clone isn't available at that phase, so anything with cd front-end, git, or pnpm install fails the setup (exit 1 → session won't start). Only do repo-independent global installs here; repo-relative work goes in the SessionStart hook (which runs later with the repo at $CLAUDE_PROJECT_DIR).

Node, pnpm, Postgres, Docker binaries and ripgrep are pre-installed. The script adds the Supabase CLI and the Playwright browser (globally, so no repo needed):

#!/bin/bash
npm install -g supabase
npm install -g [email protected]   # keep in sync with front-end's @playwright/test
playwright install chromium        # downloads to ~/.cache/ms-playwright — cached in the snapshot
  • Do NOT install gh, do NOT start dockerd, and do NOT touch the repo here.gh isn't in Ubuntu's repos (GitHub is handled by the MCP tools + git proxy). Starting dockerd in the setup script is reported to crash the environment — the SessionStart hook starts it per session. pnpm install also lives in the hook, since the repo isn't present during setup.

  • Keep the pinned Playwright version in sync with front-end's @playwright/test (same version → the global-install browser matches what the repo's tests expect). It currently tracks ci.yml's mcr.microsoft.com/playwright:v1.62.1-noble.

  • Network access: the default Trusted list does not include cdn.playwright.dev, which Playwright 1.58 uses to download Chrome-for-Testing — so playwright install 403s on Trusted. Switch to Custom, tick "Also include default list of common package managers", and add:

    cdn.playwright.dev
    

    (If a later run 403s on a different host — Playwright sometimes redirects to a storage CDN — add that host too.) npm, Docker Hub (Supabase images), and GitHub are already covered by the default list.

  • Environment variables (.env format, no quotes — visible to anyone who can edit the environment, so local/test values only, never production). Use the same VITE_SUPABASE_URL and VITE_SUPABASE_API_KEY your local front-end/.env.local uses — these point at the local Supabase (this project runs it on port 5411, not the default 54321) so the live /verify-fe path works when Supabase boots, and they're harmless in mock mode (the value is ignored there). The VITE_SUPABASE_API_KEY is a client publishable key, safe to place here.

    No GH_TOKEN, no production secrets (there is no secrets store yet).

3. Repo-side wiring (already committed)

  • scripts/cloud-session-start.sh — SessionStart hook. No-op locally (CLAUDE_CODE_REMOTE != true); in the cloud it runs pnpm install, starts the Docker daemon (overlay2, iptables off), runs supabase start -x edge-runtime && supabase db reset (two attempts, purging stale containers before each so the retry starts from a clean stack), writes front-end/.env.local and api/.env pointed at the local instance (never overwriting an existing file), and symlinks /opt/google/chrome/chrome to the sandbox's Chromium so @playwright/mcp — which defaults to the chrome channel — can launch. All best-effort and non-fatal; the Supabase outcome is written to /tmp/cloud-supabase-status (ready|failed) for /start to branch on.
  • .claude/settings.json — registers the hook (see Apply the settings hook below if not yet present).
  • .claude/skills/start/SKILL.md — when CLAUDE_CODE_REMOTE=true, /start skips the ~/.worktrees worktree and checks out in place, uses the GitHub MCP tools + git proxy instead of gh, and verifies with live /verify-fe when Supabase booted (/tmp/cloud-supabase-status = ready) or the mock-mode suite otherwise.

Apply the settings hook

Ensure .claude/settings.json contains:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/cloud-session-start.sh" }
        ]
      }
    ]
  }
}

4. Create the trigger Routine

Create a Routine pinned to the environment from step 2, prompt:

/start <issue number>
After opening the PR, enable auto-fix on it (or run /autofix-pr on the branch).

Trigger: a GitHub event filtered to issues labelled claude.

If issue-label events aren't a supported Routine trigger yet, use one of:

  • a scheduled Routine that runs /start on the newest open claude-labelled issue (managed polling), or
  • manual kickoff from a terminal: claude --remote "/start 612".

Confirm the currently supported trigger types when you build this.

Daily use

  1. Open an issue (or label an existing one) with claude.
  2. The Routine spins up a cloud session; watch it at claude.ai/code or on mobile.
  3. /start opens a PR; Auto-fix drives ci.yml to green automatically.
  4. Review and merge the PR like any other.

Caveats (research preview)

  • Edge Functions aren't testable in-sandbox. edge-runtime is excluded (the sandbox strips CAP_SYS_RESOURCE, so it can't raise its nofile rlimit), so anything exercising a Supabase Edge Function won't run against local Supabase here. Every other service is healthy. (This repo's front-end doesn't invoke edge functions from the client, so /verify-fe is unaffected.)
  • Docker/Supabase are still best-effort. If dockerd or supabase start ever fail (e.g. an overlay2 mount issue on a changed image), the hook records failed and verification falls back to ci.yml's mock-mode suite (unit + route + Playwright e2e with fake VITE_SUPABASE_*), which needs no backend. First-session supabase start is slow (pulls ~12 images); they cache to disk for subsequent sessions.
  • No gh CLI — GitHub is reached via the built-in mcp__github__* tools and the git push proxy (scoped to the current branch).
  • Shared rate limits with all interactive Claude usage — parallel runs eat into your own quota.
  • No secrets store — env vars are visible to environment editors; test/mock keys only.
  • GitHub-only for pushing PRs.
  • Auto-fix does not resolve merge conflicts (GitHub emits no webhook for them). If the base branch advances into a conflict, open the session and ask Claude to rebase.
  • Resource ceiling: 4 vCPU / 16 GB RAM / 30 GB disk per session.

Verification checklist

  1. Start a session in the environment; run check-tools — confirm Playwright chromium is present. (gh will be absent — expected; GitHub is via MCP.)
  2. Confirm the SessionStart hook ran: node_modules/ exists at the repo root and under front-end/ (proves pnpm install executed). If it's missing, the .claude/settings.json hook isn't wired — see Apply the settings hook. Then check cat /tmp/cloud-supabase-status:
    • ready → Docker + local Supabase came up (the normal case); supabase status should list every service except edge-runtime.
    • failed → inspect /tmp/dockerd.log; verification falls back to mock-mode.
  3. Confirm front-end/.env.local and api/.env exist and point at the local Supabase API on port 5411.
  4. Confirm ls -l /opt/google/chrome/chrome resolves to an executable — without it the Playwright MCP server can't launch and every browser-driven skill (/verify-fe, /ui-review, the ui-designer agent) fails on first use.
  5. Label a throwaway issue claude; confirm the session checks out a branch without a ~/.worktrees worktree, verifies (live /verify-fe when Supabase is ready, else the mock-mode suite), and opens a PR via the GitHub MCP tools.
  6. Seed a failing check so ci.yml goes red once; confirm Auto-fix pushes a fix and CI goes green with no human @claude.
  7. Confirm .github/workflows/claude.yml and ci.yml are unchanged and the cloud run shows no compute charge (subscription usage only).

Fallbacks

  • .github/workflows/claude.yml — the metered self-hosted @claude path.
  • A self-hosted Docker poller (own VPS) — only if preview limits prove blocking.