--- title: "Installatieplan — Beelink Ubuntu Scrum4Me server en worker-aanpassingen" status: draft audience: [maintainer, operator, ai-agent] language: nl last_updated: 2026-05-10 --- # Installatieplan — Beelink Ubuntu Scrum4Me server en worker-aanpassingen ## Doel Deze notitie beschrijft de huidige Beelink-installatie en het vervolgplan om de Scrum4Me workers geschikt te maken voor drie rollen: ```text worker-idea worker-implementation worker-orchestrator ``` De server draait nu als LAN-host voor Scrum4Me. Productie-internettoegang met domein en HTTPS is nog een latere stap. ## Hardware | Onderdeel | Waarde | |---|---| | Machine | Beelink mini-PC | | CPU | Intel Core i5-12450H | | RAM zichtbaar in Ubuntu | 16 GB | | Max RAM volgens hardware | 32 GB | | Disk | 468 GB bruikbaar na Ubuntu-installatie | | IP | `192.168.0.154` | Opmerking: Ubuntu ziet momenteel ongeveer 16 GB RAM. De hardware meldt een maximum van 32 GB, maar dat betekent niet dat 32 GB bruikbaar/geplaatst is. ## Huidige Installatie ### Ubuntu Ubuntu Server is geïnstalleerd op de hele disk. Belangrijke keuzes: - Ubuntu Server 24.04 LTS. - Geen Ubuntu Desktop. - Geen LVM. - Geen aparte GPU-drivers. - Geen Windows dual boot meer. - Hostname: `scrum4me-server`. - Sleep/hibernate uitgeschakeld. - Swapfile vergroot naar 16 GB. Controle: ```bash hostnamectl free -h swapon --show df -h ``` ### Directorystructuur Alle service-data staat onder: ```text /srv/scrum4me ``` Structuur: ```text /srv/scrum4me/postgres database data /srv/scrum4me/repos GitHub clones /srv/scrum4me/worker-cache worker caches /srv/scrum4me/worker-logs worker logs /srv/scrum4me/worker-state worker state /srv/scrum4me/backups Postgres backups /srv/scrum4me/compose Docker Compose files /srv/scrum4me/caddy Caddy config/data ``` ### Docker Docker Engine draait native op Ubuntu. Controle: ```bash docker run hello-world docker compose version ``` ### Postgres Postgres draait als Docker container: ```text container: scrum4me-postgres image: postgres:17 ``` Host mapping: ```text 127.0.0.1:5432 -> postgres:5432 ``` Host-app gebruikt: ```env DATABASE_URL="postgresql://scrum4me:@127.0.0.1:5432/scrum4me" DIRECT_URL="postgresql://scrum4me:@127.0.0.1:5432/scrum4me" ``` Containers gebruiken: ```env DATABASE_URL=postgresql://scrum4me:@postgres:5432/scrum4me DIRECT_URL=postgresql://scrum4me:@postgres:5432/scrum4me ``` DB-test: ```bash docker exec -e PGPASSWORD="$DBPASS" scrum4me-postgres \ psql -h 127.0.0.1 -U scrum4me -d scrum4me \ -c "select current_user, current_database();" ``` ### Scrum4Me Web Repo: ```text /srv/scrum4me/repos/Scrum4Me ``` Build: ```bash cd /srv/scrum4me/repos/Scrum4Me rm -rf .next npm run build ``` Runtime: ```text systemd service: scrum4me-web ``` Service startcommand: ```bash npm run start -- -H 0.0.0.0 ``` Controle: ```bash systemctl status scrum4me-web --no-pager curl -I http://127.0.0.1:3000/login ``` ### Caddy Caddy draait als Docker container: ```text container: scrum4me-caddy ``` Caddy reverse proxyt: ```text http://192.168.0.154 -> Caddy -> 172.18.0.1:3000 -> Scrum4Me web ``` Caddyfile: ```caddyfile :80 { reverse_proxy 172.18.0.1:3000 } ``` Controle: ```bash curl -I http://192.168.0.154/login docker logs --tail=50 scrum4me-caddy ``` ### LAN Session Config Omdat de server nu via HTTP op LAN draait, is secure session cookie tijdelijk uitgezet. Env: ```env SESSION_COOKIE_SECURE="false" ``` Code-aanpassing: ```ts secure: process.env.SESSION_COOKIE_SECURE === 'true', ``` Later, bij domein + HTTPS: ```env SESSION_COOKIE_SECURE="true" ``` Daarna: ```bash rm -rf .next npm run build sudo systemctl restart scrum4me-web ``` ### Migrations De database is gemigreerd. Belangrijke migration-notitie: `20260506101436_restore_todos_table` kan op een bestaande DB falen met: ```text relation "todos" already exists ``` Voor deze server is de juiste aanpak: ```bash npx prisma migrate resolve --applied 20260506101436_restore_todos_table npx prisma migrate deploy ``` Controle: ```bash npx prisma migrate status docker exec -it scrum4me-postgres psql -U scrum4me -d scrum4me -c "\dt public.users" ``` ### Admin en Product Admin user is aangemaakt via: ```bash npx tsx scripts/create-admin.ts janpeter '' ``` Login werkt. Product aanmaken werkt. ### Backups Backup-script: ```text /srv/scrum4me/backup-postgres.sh ``` Script: ```bash #!/usr/bin/env bash set -euo pipefail BACKUP_DIR="/srv/scrum4me/backups" STAMP="$(date +%Y%m%d-%H%M%S)" FILE="$BACKUP_DIR/scrum4me-$STAMP.sql.gz" mkdir -p "$BACKUP_DIR" docker exec scrum4me-postgres pg_dump -U scrum4me scrum4me | gzip > "$FILE" find "$BACKUP_DIR" -type f -name 'scrum4me-*.sql.gz' -mtime +14 -delete echo "backup written: $FILE" ``` Test: ```bash /srv/scrum4me/backup-postgres.sh ls -lh /srv/scrum4me/backups ``` Cron: ```cron 15 3 * * * /srv/scrum4me/backup-postgres.sh >> /srv/scrum4me/backups/backup.log 2>&1 ``` ## Worker-Idea Installatie Worker compose-service: ```text worker-idea container: scrum4me-worker-idea health: http://127.0.0.1:18081/health ``` Belangrijke env-waarden: ```env SCRUM4ME_BASE_URL=http://caddy SCRUM4ME_TOKEN= DATABASE_URL=postgresql://scrum4me:@postgres:5432/scrum4me DIRECT_URL=postgresql://scrum4me:@postgres:5432/scrum4me GH_TOKEN= GH_PRECLONE_REPOS=madhura68/Scrum4Me,madhura68/scrum4me-mcp,madhura68/scrum4me-docker CLAUDE_CODE_OAUTH_TOKEN= ``` Token-validatie: ```bash read -s -p "Scrum4Me token: " TOKEN; echo curl -i -H "Authorization: Bearer $TOKEN" http://127.0.0.1:3000/api/products unset TOKEN ``` Verwacht: ```text HTTP/1.1 200 OK ``` Worker health: ```bash curl http://127.0.0.1:18081/health ``` Gezonde idle-output bevat: ```json { "status": "idle", "heartbeatAgeSeconds": 1, "consecutiveFailures": 0 } ``` ## Huidige Worker-Beperking De Docker worker is gezond, maar Scrum4Me UI toont mogelijk nog: ```text geen Claude worker actief ``` Oorzaak: - De Docker health-server draait altijd. - De daemon-loop draait altijd. - Maar de DB-tabel `claude_workers` wordt nu alleen bijgewerkt door de MCP stdio-server. - Die MCP stdio-server start pas binnen een echte Claude/MCP job-run. - Bij een lege queue is de Docker worker dus idle en gezond, maar verschijnt hij niet als actieve worker in de UI. Controle: ```bash docker exec -it scrum4me-postgres psql -U scrum4me -d scrum4me \ -c "select t.id, t.label, w.id as worker_id, w.last_seen_at from api_tokens t left join claude_workers w on w.token_id=t.id order by t.created_at desc;" ``` Gezonde Docker-worker maar lege presence: ```text label | worker_id | last_seen_at worker-idea | | ``` ## Worker Aanpassingsplan ### Doelrollen ```text worker-idea IDEA_GRILL IDEA_MAKE_PLAN PLAN_CHAT worker-implementation TASK_IMPLEMENTATION SPRINT_IMPLEMENTATION later STORY_IMPLEMENTATION worker-orchestrator PR_REVIEW CI_TRIAGE MERGE_CONFLICT_RESOLUTION REPAIR_FAILED_JOB CONTEXT_SUMMARY ``` ## Fase 1 — Presence Fix ### Probleem Worker-health is nu container-lokaal, maar UI-presence is DB-gebaseerd. Nu: ```text curl :18081/health -> online claude_workers -> leeg UI -> offline ``` ### Gewenst gedrag Zolang de Docker daemon-loop draait, moet `claude_workers.last_seen_at` vers blijven, ook als de queue leeg is. ### Aanpassing Verplaats worker-presence naar `scrum4me-docker/bin/run-one-job.ts` of naar een kleine runner-level heartbeat naast `run-agent.sh`. Aanbevolen: in `run-one-job.ts`, direct na `getAuth()`: ```ts const { userId, tokenId } = await getAuth() await registerWorker({ userId, tokenId }) const heartbeat = startHeartbeat({ userId, tokenId, intervalMs: 10_000 }) ``` In `finally`: ```ts heartbeat.stop() ``` Niet unregisteren bij normale idle-exit. Anders gaat de UI-indicator flikkeren tussen iteraties. ### Acceptatie Bij lege queue: ```bash curl http://127.0.0.1:18081/health ``` toont: ```text status idle ``` En: ```sql select token_id, last_seen_at, now() - last_seen_at from claude_workers; ``` toont een recente `last_seen_at`. ## Fase 2 — Role-Aware Workers ### Probleem De huidige worker claimt elke job die beschikbaar is. Daardoor kan `worker-idea` ook implementation jobs claimen. ### Nieuwe env ```env SCRUM4ME_WORKER_ROLE=idea ``` Toegestane waarden: ```text idea implementation orchestrator ``` ### Claimfilter `tryClaimJob` krijgt een role/capability-filter. Mapping: ```text idea: IDEA_GRILL IDEA_MAKE_PLAN PLAN_CHAT implementation: TASK_IMPLEMENTATION SPRINT_IMPLEMENTATION orchestrator: PR_REVIEW CI_TRIAGE MERGE_CONFLICT_RESOLUTION REPAIR_FAILED_JOB CONTEXT_SUMMARY ``` ### Acceptatie Test: - Queue bevat één `IDEA_GRILL` en één `TASK_IMPLEMENTATION`. - Alleen `worker-idea` actief: claimt alleen `IDEA_GRILL`. - Alleen `worker-implementation` actief: claimt alleen `TASK_IMPLEMENTATION`. - Beide actief: ieder claimt eigen jobtype. ## Fase 3 — DB/UI Uitbreiding Breid `claude_workers` uit met: ```text role worker_name container_name last_status last_job_id last_error ``` UI toont dan: ```text Idea worker online / idle Implementation worker offline Orchestrator online / idle ``` ## Fase 4 — Orchestrator Jobs Nieuwe job kinds: ```text PR_REVIEW CI_TRIAGE MERGE_CONFLICT_RESOLUTION REPAIR_FAILED_JOB CONTEXT_SUMMARY ``` Orchestrator mag: - PR's inspecteren. - CI-fouten samenvatten. - Merge conflicts analyseren. - Repair jobs aanmaken. - Context capsules schrijven. - Human escalation vragen. - Draft PR naar ready begeleiden. Orchestrator mag niet: - Vrij featurewerk implementeren. - Dezelfde branch tegelijk wijzigen als implementation-worker. - Auto-mergen zonder checks. - Secrets of tokens loggen. ## Fase 5 — Deployment Na code-aanpassing: ```bash cd /srv/scrum4me/repos/scrum4me-docker git pull cd /srv/scrum4me/compose docker compose build worker-idea docker compose up -d --force-recreate worker-idea ``` Checks: ```bash curl http://127.0.0.1:18081/health docker logs -f scrum4me-worker-idea docker exec -it scrum4me-postgres psql -U scrum4me -d scrum4me \ -c "select token_id, last_seen_at from claude_workers;" ``` ## Aanbevolen Volgorde Vanaf Nu 1. Test één `IDEA_GRILL` job met de huidige worker. 2. Implementeer Fase 1: runner-level presence. 3. Rebuild `worker-idea`. 4. Verifieer UI online/idle bij lege queue. 5. Implementeer Fase 2: role-aware claiming. 6. Voeg `worker-implementation` toe. 7. Voeg pas daarna `worker-orchestrator` toe. Niet meteen drie workers starten zonder role-aware claimfilter.