run-one-job.ts scant de volledige stream-json output (incl. álle
tool-results) op auth-error-patronen, en run-agent.sh grept hetzelfde
over het complete run-log — beide zonder de exit-code te checken.
Daardoor legt een geslaagde job (exit 0, result.is_error=false) de
worker plat zodra z'n output toevallig iets als "401 unauthorized"
bevat — bv. wanneer de agent een doc over route-handler-auth leest of
een endpoint test. run-agent.sh doet dan touch TOKEN_EXPIRED + sleep
infinity en de worker draait pas na een rebuild weer.
Fix: detectie gaten op een niet-nul exit. Een echte credential-fout
laat 'claude' non-zero exiten, dus echte expiries worden nog steeds
gevangen — alleen de false positives op geslaagde runs verdwijnen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
run-one-job.ts spawnde Claude met een hardcoded --output-format text,
dus de run-log bevatte alleen Claude's eind-samenvatting — geen zicht op
het werk tijdens een job (~6-10 min stilte, dan ineens de samenvatting).
- --output-format komt nu uit AGENT_CLAUDE_OUTPUT_FORMAT (default
'stream-json'). stream-json streamt elke tool-call / elk bericht live
naar de run-log; --verbose wordt automatisch toegevoegd want
print-mode vereist dat bij stream-json.
- Zet AGENT_CLAUDE_OUTPUT_FORMAT=text terug voor de oude terse output.
- .env.example: nieuwe var gedocumenteerd.
stdoutBuf wordt alleen voor de TOKEN_EXPIRED-regexscan gebruikt; de
auth-error-strings staan ook binnen de JSON-events, dus detectie werkt
ongewijzigd. Niets parseert de output als job-resultaat.
Gevolg: de run-log (en de jobs/<job_id>.log symlink uit IDEA-063) wordt
JSONL i.p.v. plain text — gebruik jq of een viewer. Log-grootte groeit;
rotate-logs.sh dekt dat al af.
node --check + type-strip schoon.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run-logs in /var/log/agent/runs/ zijn timestamp-named, dus de output van
een specifieke job was alleen via grep te vinden. De map jobs/ bestond al
maar werd niet gevuld.
- run-agent.sh: geeft het run-log-pad door als RUN_LOG env-var aan
run-one-job.ts.
- run-one-job.ts: legt direct na de claim een symlink
jobs/<job_id>.log -> ../runs/<ts>.log. Relatief pad (overleeft de
host bind-mount), best-effort (faalt de job nooit over een log-gemak).
- log-cleanup.sh: ruimt dangling per-job symlinks op met `find -xtype l`
— nodig omdat rotate-logs.sh het doel na 24u gzipt (.log -> .log.gz)
of na 30d verwijdert, en de bestaande `-type f` cleanup symlinks niet
raakt.
Functioneel geverifieerd: symlink resolveert, dangling-prune werkt,
`-type f` negeert de symlink (geen voortijdige delete). run-one-job.ts
parseert schoon (node --check + type-strip).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tot nu toe schreef de NAS-runner nooit naar `claude_workers`, waardoor
de UI de worker als offline toonde ondanks gezonde container-health.
Direct na `getAuth()` doen we nu een UPSERT via `registerWorker` en
starten we een 10s heartbeat die `last_seen_at` vers houdt tijdens
quota-backoff, LISTEN-wait, claude-spawn en cleanup.
De heartbeat stopt via try/finally op elk exit-pad. Bewust geen
`unregisterWorker`: tussen iteraties zou dat UI-flicker geven, en
abnormale exits worden door de UI's eigen 60s-prune opgevangen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
run-one-job.ts importeerde releaseLocksOnTerminal uit
'/opt/scrum4me-mcp/src/tools/wait-for-job.js' maar die module re-exporteert
deze symbol niet (alleen lokaal geïmporteerd uit ../git/job-locks.js).
Resultaat: bij elke rollbackClaim-pad (worktree-fout, getFullJobContext-
fout, claude exit≠0 zonder update_job_status) crasht run-one-job met:
TypeError: (0 , import_wait_for_job.releaseLocksOnTerminal) is not a function
Fix: importeer direct uit /opt/scrum4me-mcp/src/git/job-locks.js (zelfde
pad als wait-for-job.ts en cancel/pbi-cascade.ts intern doen).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drie kleine fixes voor de runner uit PBI-4 die in een lokale smoke-test
naar boven kwamen:
1. NODE_PATH=/opt/scrum4me-mcp/node_modules in Dockerfile ENV — anders
vindt tsx de top-level `pg` import in bin/run-one-job.ts niet (resolve
start vanaf /opt/agent/bin/, zoekt geen scrum4me-mcp/node_modules).
2. ARG MCP_CACHE_BUST in Dockerfile vóór de scrum4me-mcp clone-laag.
BuildKit cached anders de clone op MCP_GIT_REF=main, ook als main
intussen nieuwere commits heeft. Rebuild met
`--build-arg MCP_CACHE_BUST=$(date +%s)` invalidate't deze laag
deterministisch.
3. quotaProbe in run-one-job.ts soft-failt nu bij niet-zero exit, geen
pct-veld, of geen rate-limit-headers in response. De Anthropic API
retourneert niet altijd headers; dit zou de runner niet hard moeten
crashen. Komt overeen met CLAUDE.md stap 0.4 ("anders: ga door").
Lokale smoke-test bevestigt nu dat een IDEA_GRILL job correct geclaimd
wordt met `--model=claude-sonnet-4-6 --permission-mode=plan --effort=high`
en de juiste 10 allowed_tools.
Apart probleem ontdekt (NIET in deze PR): IDEA_GRILL/IDEA_MAKE_PLAN/
PLAN_CHAT draaien default in --permission-mode plan. In autonomous batch-
mode kan Claude in plan-mode mogelijk geen update_job_status aanroepen
(plan-mode wacht op human approval), waardoor jobs FAILED raken na
2x lease-expiry. Verdient eigen issue/PR voor permission_mode review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vervangt de lange seed-prompt-loop door een Node-runner die per iteratie
precies één geclaimde job afhandelt. Eén Claude-invocation = één job met
de juiste per-kind config (model/permission-mode/effort/allowed_tools)
volgens PBI-67's resolveJobConfig.
- T-18/19/20/21: bin/run-one-job.ts (nieuw, ESM tsx). Imports direct uit
/opt/scrum4me-mcp/src/. Stappen: auth → quota-probe → claim met
LISTEN-fallback 270s → getFullJobContext → attachWorktreeToJob (TASK)
→ payload schrijven → CLI-args bouwen + mapBudgetToEffort → spawn claude
→ token-expiry detection → rollbackClaim bij exit≠0 zonder
update_job_status → cleanup. Logging met ISO-timestamps voor elke fase.
setInterval(60s) lease-renewal alleen voor SPRINT_IMPLEMENTATION.
- T-22: bin/run-agent.sh — SEED_PROMPT + ALLOWED_TOOLS verwijderd; claude
-p vervangen door `tsx /opt/agent/bin/run-one-job.ts`. TOKEN_EXPIRED
detectie uitgebreid met exit_code==3 trigger.
- T-23: CLAUDE.md herschreven — operationele loop weg, architectuur-
uitleg toegevoegd, hardstop-regels (geen wait_for_job, check_queue_empty,
job_heartbeat, git push).
T-24 smoke-test gedeferd tot na merge scrum4me-mcp PR (Dockerfile clone't
via MCP_GIT_REF, default 'main'); zie test_result-log voor verificatie-
commando's.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>