scrum4me-docker/README.md

6.7 KiB
Raw Blame History

scrum4me-agent-runner

Headless Claude Code worker die de Scrum4Me job-queue (M13) leegtrekt vanaf een QNAP NAS via Container Station. Geen Vercel, geen browser, geen toetsenbord — Claude Code draait als daemon, claimt jobs uit mcp__scrum4me__wait_for_job, voert ze uit in een per-job clone, en pusht nooit zelf.

Architectuur in één plaatje

┌─ QNAP TS-664 (Container Station) ─────────────────────────────┐
│                                                                │
│  ┌─ container: agent-runner ────────────────────────────────┐  │
│  │  PID 1: tini → run-agent.sh (daemon-loop)                │  │
│  │            ├─ health-server.js  (8080 → host)            │  │
│  │            └─ claude -p (per-batch, met MCP via stdio)   │  │
│  │                  └─ scrum4me-mcp → Neon Postgres         │  │
│  │                                                          │  │
│  │  /tmp/job-<id>      ephemeral working trees              │  │
│  │  /var/cache/repos   bare git mirrors  (volume)           │  │
│  │  /var/cache/npm     npm cache         (volume)           │  │
│  │  /var/log/agent     run + job logs    (volume)           │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                │
│  /share/Agent/cache  /share/Agent/logs  /share/Agent/state     │
└────────────────────────────────────────────────────────────────┘
                              │
                              ▼  HTTPS
                   Neon Postgres (Scrum4Me DB)
                              ▲
                              │
                   Vercel ─── Scrum4Me UI (gebruikers enqueueen jobs)

Eén claude -p-invocation roept intern wait_for_job aan totdat de queue leeg is (≈600 s lege block-time → afsluiten). De wrapper start claude -p opnieuw zodra hij eindigt, met exponentiële backoff bij fouten.

Wat zit waar

Bestand Doel
Dockerfile Ubuntu 22.04 + Node 22 + Claude Code + scrum4me-mcp + scripts
docker-compose.yml Service-definitie, volumes, env-file, restart-policy, limits
package.json Npm-dependencies van de runner zelf (alleen scrum4me-mcp pin)
mcp-config.json Claude Code MCP-config (verwijst stdio naar scrum4me-mcp)
CLAUDE.md Agent-rol-instructies, auto-geladen door claude -p
bin/entrypoint.sh Container-startup: dirs, health-server, daemon-loop
bin/run-agent.sh Daemon-loop met backoff, exit-code-routing en state-writes
bin/check-tokens.sh Pre-flight: API-token, OAuth-token, DB-bereikbaarheid
bin/job-prepare.sh Per-job: bare-fetch + clone-via-reference naar /tmp/job-<id>
bin/job-cleanup.sh Per-job: logs naar /var/log, working tree weg
bin/health-server.js HTTP-endpoint op 8080 dat state.json en marker-files leest
bin/rotate-logs.sh Compress/cleanup van oude .log-bestanden
.env.example Alle env-vars met uitleg

Vereisten op de NAS

  • Container Station 2+ (Docker compose v2)
  • Drie shares aangemaakt: /share/Agent/cache, /share/Agent/logs, /share/Agent/state
  • Of één share /share/Agent waaronder de drie subdirs vallen
  • Internet-uitgang naar api.anthropic.com, github.com, je Neon-host, registry.npmjs.org

Deploy

# 1. Op je werkstation: token's regelen
#    a. CLAUDE_CODE_OAUTH_TOKEN  →  draai `claude setup-token` (browser-flow)
#    b. SCRUM4ME_TOKEN           →  log in als de dedicated agent-user in
#                                   Scrum4Me, /settings/tokens, label "NAS-runner"
#    c. DATABASE_URL/DIRECT_URL  →  Neon dashboard

# 2. Repo op de NAS plaatsen
ssh admin@nas
cd /share/Agent
git clone https://github.com/<jij>/scrum4me-agent-runner.git
cd scrum4me-agent-runner

# 3. Env aanmaken
cp .env.example .env
chmod 600 .env
vi .env   # vul alle waarden in

# 4. Build + start
docker compose build
docker compose up -d

# 5. Verifiëren
curl http://nas.local:8080/health
docker compose logs -f

Updaten (handmatig, bewust)

SCRUM4ME_TOKEN of CLAUDE_CODE_OAUTH_TOKEN rouleer je via een rebuild:

cd /share/Agent/scrum4me-agent-runner
git pull
vi .env                   # nieuwe waarden
docker compose build      # nieuwe scrum4me-mcp-versie als dat veranderd is
docker compose up -d

Dezelfde flow voor schema-drift in scrum4me-mcp: pin een nieuwe MCP_GIT_REF in .env of in docker-compose.yml, rebuild.

Health-endpoint

GET http://<nas>:8080/health retourneert:

{
  "status": "running",            // running | idle | unhealthy | token-expired
  "lastBatchAt": "2026-05-01T12:34:56Z",
  "lastBatchExit": 0,
  "consecutiveFailures": 0,
  "tokenStatus": { "anthropic": "ok", "scrum4me": "ok", "db": "ok" }
}

HTTP-status: 200 als running/idle, 503 bij token-expired of als de laatste heartbeat ouder is dan 5 minuten.

Filesystem-grenzen

De agent-user heeft geen SSH-keys, geen ~/.gitconfig met push-credentials, en geen toegang tot andere shares dan /share/Agent/*. Commits worden lokaal in de per-job clone gemaakt; pushen gebeurt door jou op je werkstation na review (CLAUDE.md regel: "git push is altijd expliciet").

Bekende grenzen

  • Eén actieve job tegelijk. De wrapper-loop is sequentieel. Voor parallellisme zou je meerdere containers met dezelfde SCRUM4ME_TOKEN kunnen draaien — wait_for_job gebruikt FOR UPDATE SKIP LOCKED dus dat is veilig op DB-niveau, maar dan moet je je node_modules-cache per container scheiden.
  • OAuth-token: 1 jaar geldig. Bij verloop schrijft de wrapper een TOKEN_EXPIRED-marker en wordt de container unhealthy. Geen auto-rotatie.
  • npm install per job kost op een N5095 ~3060 s per Next.js-clone, óók met de pnpm-store. Voor zeer kleine fixes is dat de dominante factor. Kan later vervangen worden door een persistente warm-node_modules per repo als dat een knelpunt wordt.