Spiegelt de scrum4me-mcp wijzigingen naar de Scrum4Me web-app zodat enqueue-laag (lib/job-config-snapshot.ts) en claim-laag dezelfde defaults gebruiken. Plus runbook-correctie van een eerder gedocumenteerde maar niet-bestaande Claude CLI-flag. - T-25: lib/job-config.ts — mapBudgetToEffort export + KIND_DEFAULTS .allowed_tools voor TASK/SPRINT/IDEA_GRILL/IDEA_MAKE_PLAN omgezet naar expliciete lijsten zonder wait_for_job/check_queue_empty/ get_idea_context. Comment-block over CLI-flag-mapping en sync met scrum4me-mcp. - T-26: docs/runbooks/worker-idempotency.md sectie "Config doorgeven aan Claude Code (PBI-67)" herschreven. --thinking-budget vervangen door --effort (mapping-tabel toegevoegd); --max-turns geschrapt (CLI heeft die flag niet — audit-only). Sectie "Wie doet wat in de runner- architectuur" toegevoegd. - T-27: docs/runbooks/job-model-selection.md — notes over max_turns, thinking_budget en allowed_tools onder de matrix. Nieuwe sectie "Runner-architectuur" met verwijzing naar plan + worker-idempotency. - T-28: __tests__/lib/job-config.test.ts (nieuw) — 22 tests: mapBudgetToEffort grenswaarden + KIND_DEFAULTS.allowed_tools structurele checks + cascade regression. Plus: docs/plans/queue-loop-extraction.md (geschreven in plan-mode, nu gepubliceerd in repo). Verify: lint OK, typecheck OK, 587 tests in 78 files passed. Build niet lokaal uitgevoerd (vereist DATABASE_URL voor "Collecting page data" — diff raakt geen API-route). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import {
|
|
getKindDefault,
|
|
resolveJobConfig,
|
|
mapBudgetToEffort,
|
|
} from '@/lib/job-config'
|
|
|
|
describe('mapBudgetToEffort', () => {
|
|
it.each([
|
|
[0, null],
|
|
[-1, null],
|
|
[1, 'medium'],
|
|
[3000, 'medium'],
|
|
[6000, 'medium'],
|
|
[6001, 'high'],
|
|
[9000, 'high'],
|
|
[12000, 'high'],
|
|
[12001, 'xhigh'],
|
|
[18000, 'xhigh'],
|
|
[24000, 'xhigh'],
|
|
[24001, 'max'],
|
|
[50000, 'max'],
|
|
[100000, 'max'],
|
|
])('budget %i → %s', (budget, expected) => {
|
|
expect(mapBudgetToEffort(budget)).toBe(expected)
|
|
})
|
|
})
|
|
|
|
describe('KIND_DEFAULTS.allowed_tools — sync met scrum4me-mcp', () => {
|
|
it('TASK_IMPLEMENTATION bevat geen claim-tools', () => {
|
|
const cfg = getKindDefault('TASK_IMPLEMENTATION')
|
|
expect(cfg.allowed_tools).not.toContain('mcp__scrum4me__wait_for_job')
|
|
expect(cfg.allowed_tools).not.toContain('mcp__scrum4me__check_queue_empty')
|
|
expect(cfg.allowed_tools).not.toContain('mcp__scrum4me__get_idea_context')
|
|
})
|
|
|
|
it('TASK_IMPLEMENTATION bevat de essentiële task-tools', () => {
|
|
const cfg = getKindDefault('TASK_IMPLEMENTATION')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__update_task_status')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__update_job_status')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__verify_task_against_plan')
|
|
expect(cfg.allowed_tools).toContain('Bash')
|
|
expect(cfg.allowed_tools).toContain('Edit')
|
|
expect(cfg.allowed_tools).toContain('Write')
|
|
})
|
|
|
|
it('SPRINT_IMPLEMENTATION bevat sprint-specifieke tools maar GEEN job_heartbeat (runner doet die)', () => {
|
|
const cfg = getKindDefault('SPRINT_IMPLEMENTATION')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__update_task_execution')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__verify_sprint_task')
|
|
expect(cfg.allowed_tools).not.toContain('mcp__scrum4me__job_heartbeat')
|
|
})
|
|
|
|
it('IDEA_GRILL bevat update_idea_grill_md en geen wait_for_job', () => {
|
|
const cfg = getKindDefault('IDEA_GRILL')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__update_idea_grill_md')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__log_idea_decision')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__update_job_status')
|
|
expect(cfg.allowed_tools).not.toContain('mcp__scrum4me__wait_for_job')
|
|
})
|
|
|
|
it('IDEA_MAKE_PLAN bevat update_idea_plan_md en geen wait_for_job', () => {
|
|
const cfg = getKindDefault('IDEA_MAKE_PLAN')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__update_idea_plan_md')
|
|
expect(cfg.allowed_tools).toContain('mcp__scrum4me__log_idea_decision')
|
|
expect(cfg.allowed_tools).not.toContain('mcp__scrum4me__wait_for_job')
|
|
})
|
|
|
|
it('alle kinds hebben non-null allowed_tools', () => {
|
|
for (const kind of [
|
|
'IDEA_GRILL',
|
|
'IDEA_MAKE_PLAN',
|
|
'PLAN_CHAT',
|
|
'TASK_IMPLEMENTATION',
|
|
'SPRINT_IMPLEMENTATION',
|
|
]) {
|
|
const cfg = getKindDefault(kind)
|
|
expect(cfg.allowed_tools).not.toBeNull()
|
|
expect(Array.isArray(cfg.allowed_tools)).toBe(true)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('resolveJobConfig — cascade (regression)', () => {
|
|
it('task.requires_opus overrult product.preferred_model', () => {
|
|
const cfg = resolveJobConfig(
|
|
{ kind: 'TASK_IMPLEMENTATION' },
|
|
{ preferred_model: 'claude-sonnet-4-6' },
|
|
{ requires_opus: true },
|
|
)
|
|
expect(cfg.model).toBe('claude-opus-4-7')
|
|
})
|
|
|
|
it('product.preferred_permission_mode overrult bypassPermissions', () => {
|
|
const cfg = resolveJobConfig(
|
|
{ kind: 'TASK_IMPLEMENTATION' },
|
|
{ preferred_permission_mode: 'acceptEdits' },
|
|
)
|
|
expect(cfg.permission_mode).toBe('acceptEdits')
|
|
})
|
|
})
|