* fix(KIND_DEFAULTS): permission_mode acceptEdits voor idea-kinds + PLAN_CHAT
Spiegel van scrum4me-mcp PR #40. Symptoom: IDEA_GRILL job IDEA-047 werd
3x geclaimd, Claude liep telkens succesvol (exit 0 na 600-900s) maar
deed nooit update_job_status('done'). Lease verliep, retry_count >= 2 →
status FAILED met "agent did not complete job within 2 attempts".
Root cause: KIND_DEFAULTS.permission_mode='plan' voor idea-kinds en
PLAN_CHAT. In autonome batch-mode wacht plan-mode op een human "go" na
elke planning-fase — geen mens in de loop in deze runner-context.
Fix:
- IDEA_GRILL.permission_mode: plan → acceptEdits
- IDEA_MAKE_PLAN.permission_mode: plan → acceptEdits
- PLAN_CHAT.permission_mode: plan → acceptEdits
- PLAN_CHAT.allowed_tools krijgt mcp__scrum4me__update_job_status (ontbrak)
De allowed_tools-lijsten doen de echte sandboxing (geen Bash, geen Edit
voor IDEA_GRILL/PLAN_CHAT). Plan-mode's "veiligheid" wordt al door
tool-allowlists geleverd; acceptEdits is hier puur om Claude door zijn
eigen update_job_status loop te laten lopen zonder approval-wachttijd.
Plus: docs/runbooks/{job-model-selection,worker-idempotency}.md tabellen
bijgewerkt. last_updated note in job-model-selection.md.
Verify: 587 tests in 78 files passed (incl. nieuwe lib/job-config tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: remove .claude/scheduled_tasks.lock per ongeluk meegecommit
Lokale tooling-lock-file van de cowork-skills, hoort niet in de repo.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
* ST-cmowjelb1: Parser: bestand-relatieve regel + hint-detectie in YAMLParseError-tak
- Voeg `hint?: string` toe aan PlanParseError type
- Bereken bestand-relatief regelnummer (yamlLine + 1 voor de openings-`---`)
- Detecteer markdown-patronen (numbered/bullet lijst) op de offending regel
- Zet Nederlandstalige hint bij markdown-match
- Render hint als "Tip: …" onder het foutbericht in IdeaMdEditor
* ST-cmowjeq3q: UI: render hint apart onder error-message in IdeaMdEditor
Vervang <span block mt-0.5 text-status-blocked/80> door <div mt-1 text-foreground/80>
voor de Tip-hint per plan-spec (MD3-token, geen status-kleur).
* ST-cmowjewfg: Test: parser geeft hint bij markdown-in-frontmatter
Voeg twee Vitest-cases toe:
- hints when markdown sneaks into frontmatter: fixture met [unclosed op
een genummerde markdown-regel triggert YAMLParseError op die regel
(plain lijst zonder unclosed flow parset als geldig YAML)
- omits hint for non-markdown yaml errors: unclosed bracket zonder
markdown-patroon geeft geen hint
* feat(PBI-67/ST-1297): datamodel-velden voor job-model-selectie
Voegt 8 nieuwe optionele velden toe verspreid over Product, Task en
ClaudeJob ten dienste van de override-cascade:
task.requires_opus → job.requested_* → product.preferred_* → kind-default
Bestaande rijen krijgen NULL (Product/ClaudeJob) of false (Task) en
vallen daarmee terug op de kind-defaults uit de resolver (ST-1298).
Migration is additief: alleen ALTER TABLE ADD COLUMN, geen RENAME of
DROP. Bestaande factories en seed-script blijven werken zonder
aanpassing omdat alle nieuwe velden default-waardes hebben.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-67/ST-1299): job-config snapshot bij enqueue + worker-flag-runbook
T-789: Snapshot van resolved JobConfig in ClaudeJob.requested_*
bij elke job-creatie. Helper in lib/job-config-snapshot.ts laadt
product (preferred_*) en task (requires_opus) en draait de resolver
uit lib/job-config.ts (mirror van scrum4me-mcp/src/lib/job-config.ts —
zelfde matrix, sync-comment in bestand). Toegepast op alle 5
enqueue-locaties:
- actions/user-questions.ts (PLAN_CHAT)
- actions/sprint-runs.ts × 3 (SPRINT_IMPLEMENTATION x2,
TASK_IMPLEMENTATION loop)
- actions/ideas.ts (IDEA_GRILL / IDEA_MAKE_PLAN)
Test-mocks uitgebreid met product.findUnique en task.findUnique zodat
de helper bij unit tests veilig terugvalt op kind-defaults (alle 563
tests groen).
T-790: Sectie 'Config doorgeven aan Claude Code' toegevoegd aan
docs/runbooks/worker-idempotency.md met CLI-flag-mapping en de
verwachte aanroep per kind. Forward-link naar
docs/runbooks/job-model-selection.md (volgt in T-794).
Plus: docs/plans/job-model-selection.md (de approved plan-doc).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-67/ST-1300): cost-attribution voor thinking-tokens + admin UI
T-792: token-stats + token-history rekenen actual_thinking_tokens nu
mee in de totale kosten (tegen input-rate, conform Anthropic billing).
COALESCE-veilig zodat oude rijen 0 bijdragen i.p.v. NaN. Nieuwe export
`getTokenStatsByKind` aggregeert tokens en kosten per ClaudeJob.kind
zodat we relatieve uitgaven van IDEA_GRILL/IDEA_MAKE_PLAN/PLAN_CHAT/
TASK_IMPLEMENTATION/SPRINT_IMPLEMENTATION kunnen zien.
T-793: admin/jobs Kosten-tabel toont:
- Nieuwe kolom 'Thinking' (aantal verbruikte thinking-tokens)
- Mismatch-marker (rood) als requested_model afwijkt van actuele
model_id — duidt op een worker die de CLI-flag niet doorgaf.
Tooltip toont aangevraagd model. Geen Sentry/log-noise.
Page-level cost-berekening volgt dezelfde formule (input_price ×
thinking_tokens). 563 tests groen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(PBI-67/ST-1301): runbook + CLAUDE.md updates voor model/mode-selectie
T-794: Nieuwe runbook docs/runbooks/job-model-selection.md met
override-cascade, kind-default-matrix, override-voorbeelden,
auditspoor en cost-attribution-formule. 107 regels.
T-795: CLAUDE.md hardstop-bullet voor 'Model/mode per ClaudeJob'
(verwijst naar nieuwe runbook) + patterns-quickref-rij voor
job-config resolver. CLAUDE.md blijft 139 regels (≤ 150).
T-796: docs:check-links groen — 108 files, geen broken links. Twee
externe-repo verwijzingen (scrum4me-mcp/...) ge-de-linked tot plain
text omdat de check-links script de zustertree niet traverseert; de
referenties blijven leesbaar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nieuw script `npm run db:sync-model-prices` haalt de actuele Claude 4.x
modellijst op bij de Anthropic API en upsert prijzen in `model_prices`.
Anthropic biedt geen prijs-API, dus prijzen blijven onderhouden in een
PRICE_TABLE constante in het script. Cache-tier-prijzen worden afgeleid
via vaste multipliers (read 0.1x, write 1.25x). Nieuwe Claude 4.x modellen
worden gedetecteerd en gelogd als warning zodat duidelijk is wanneer de
tabel handmatig moet worden bijgewerkt.
- scripts/sync-model-prices.ts: idempotent upsert, --dry-run, retry op 5xx
- ANTHROPIC_API_KEY als optional env-var (.env.example, lib/env.ts)
- scripts/README.md: gebruiksinstructies + edge cases
- docs/plans/sync-model-prices.md: ontwerpdocument
Verificatie: `npm run lint`, `vitest` (563/563), TypeScript clean.
Echt gedraaid tegen DB: 3 created (eerste run) -> 3 unchanged (tweede run).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: verplaats sprint-switcher van NavBar naar product-header
Sprint-pulldown zit nu in de bestaande balk op de product backlog
(naast Sprint starten / Instellingen) i.p.v. in het midden van de
NavBar. Alleen zichtbaar wanneer het product ook het actieve product
van de gebruiker is.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync package-lock.json version naar 1.2.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: centreer sprint-switcher en verwijder badges uit dropdown items
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: vervang sprint-status badge door subtle tekst
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: toon code + titel + status in sprint-switcher dropdown items
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: cookie-write uit Server Component (Next.js 16 verbiedt dit)
setActiveSprintCookie werd direct aangeroepen in app/(app)/products/[id]/sprint/[sprintId]/page.tsx,
wat in Next.js 16 een runtime-error oplevert ('Cookies can only be modified in a Server Action
or Route Handler'). Vervangen door een client-side bridge die syncActiveSprintCookieAction
aanroept na mount, zodat de active-sprint cookie nog steeds gesynced blijft met de URL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: filter 'toon afgeronde sprints' in sprint-switcher dropdown
Default verbergt de switcher gesloten/gearchiveerde/mislukte sprints
(toont alleen open + de huidige actieve sprint). Toggle bovenaan de
lijst om alle sprints te tonen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: nieuwe sprint wordt direct geselecteerd zonder redirect
createSprintAction zet nu de active-sprint cookie naar de zojuist
aangemaakte sprint, en de StartSprintButton refresht de huidige
pagina i.p.v. te redirecten naar /sprint. Resultaat: gebruiker blijft
op de product backlog en ziet de nieuwe sprint direct geselecteerd
in de sprint-pulldown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: verplaats Manual en Admin naar user-menu dropdown
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: voeg geselecteerde PBI automatisch toe aan nieuwe sprint
Bij sprint-aanmaak wordt de pbi_id uit de selection-store als hidden
form-field meegestuurd. Server-side worden alle stories van die PBI
(zonder sprint) en hun taken aan de nieuwe sprint gekoppeld; stories
krijgen status IN_SPRINT met incrementele sort_order.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: sprint-switcher op solo- en sprint-board pagina's
Sprint-switcher is nu beschikbaar op de drie hoofdpagina's: product
backlog, solo board en sprint board. Allen renderen 'm in een
gecentreerde balk net onder de NavBar. Sprint-data via gedeelde helper
getSprintSwitcherData.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ST-cmovs79lt: Schema + migratie PushSubscription model
Voeg PushSubscription model toe aan prisma/schema.prisma met
snake_case-conventie, relation field op User, en bijbehorende
migratie (push_subscriptions tabel, FK + index op user_id).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs7e3o: web-push dependency + VAPID env vars feature-gated
Voeg web-push + @types/web-push toe aan package.json.
Registreer NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY,
VAPID_SUBJECT en INTERNAL_PUSH_SECRET als .optional() in lib/env.ts.
Documenteer alle vier in .env.example en README.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs7jgr: lib/push-server.ts met sendPushToUser + stale-cleanup
Server-only push-lib met VAPID feature-gate, send naar alle
subscriptions van een user, en automatische cleanup bij 404/410.
Unit tests: success-pad, 410 verwijdert sub, 404 verwijdert sub,
andere errors loggen zonder delete.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs7ouz: lib/push-client.ts client-side push helpers + stub actions/push.ts
Client-side helpers: isPushSupported, isIOSSafari, isStandalonePWA,
urlBase64ToUint8Array, subscribeToPush, unsubscribeFromPush.
Stub actions/push.ts zodat imports resolven (implementatie volgt
in volgende taak). Unit tests voor urlBase64ToUint8Array.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs7ut4: actions/push.ts subscribeToPushAction + unsubscribeFromPushAction
Vervangt stub met volledige implementatie: requireUser via getSession,
demo-block, Zod-validatie, upsert met user_id-scoping en user-scoped
deleteMany. Tests (8): idempotentie, demo-block, unauthenticated, invalid input.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs80c1: POST /api/internal/push/send met constant-time Bearer check
Route: 503 als INTERNAL_PUSH_SECRET uitstaat, 401 bij verkeerd secret
(timingSafeEqual), 400 bij invalid JSON, 422 bij Zod-fout, 204 bij succes.
push-server.ts: env-import vervangen door process.env om SESSION_SECRET
validatie tijdens build te omzeilen. Tests aangepast.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs862j: Admin test-send route + public/sw.js service worker
POST /api/internal/push/test-send: requireAdmin check (redirect bij
niet-admin), optioneel body met defaults, roept sendPushToUser aan, 204.
public/sw.js: push-handler met showNotification, notificationclick met
same-origin guard, focus bestaand venster of openWindow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs8jvq: PushToggle component met 3 states + iOS-banner
Client component met states loading/unsupported/ios-needs-install/
denied/subscribed/unsubscribed. useEffect detecteert initial status,
permission-prompt alleen via user-click. iOS-banner NL, denied-uitleg,
subscribe/unsubscribe knoppen met sonner-toasts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs8psg: notifications-sheet + iOS meta-tags in layout
notifications-sheet.tsx: PushToggle onderin met sectie
'Notificatie-instellingen' en visuele scheidslijn.
app/layout.tsx: appleWebApp.capable, statusBarStyle en
mobile-web-app-capable meta-tags toegevoegd via Next.js Metadata API.
manifest.json had al display: standalone.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ST-cmovs8vxj: docs/patterns/web-push.md pattern-documentatie
Architectuur-diagram, payload-shape, foutcodes, VAPID-config,
iOS-quirks, demo-users blokkade, trigger-voorbeelden (server +
HTTP) en admin-testroute curl-voorbeeld.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Voegt een verplicht code-veld toe aan Sprint, sequentieel per product
(consistent met PBI-N, ST-NNN, T-N).
- **Schema** — `Sprint.code String @db.VarChar(30)` + `@@unique([product_id, code])`
- **Migratie** — voegt kolom toe als nullable, backfillt bestaande sprints
via `ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY created_at)`
als `SP-N`, en zet daarna NOT NULL + UNIQUE.
- **Generator** — `generateNextSprintCode(productId)` in lib/code-server.ts
volgt het patroon van story/pbi/task; createSprintAction gebruikt
`createWithCodeRetry` voor race-bescherming.
- **Seed** — sprint-counter per product (`SP-1`, `SP-2`, ...).
Zichtbaar in:
- Sprint-header (`Product › Sprint actief · SP-3`)
- JobCard + JobDetailPane voor SPRINT_IMPLEMENTATION jobs
- Insights: VelocityChart x-axis (compacter dan goal-truncated),
AlignmentTrend tooltip, SprintInfoStrip
- actions/jobs-page.ts: `sprintCode` is weer een echte code i.p.v. null
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(PBI-58): add developer manual chapters under docs/manual/
Adds a 7-file English-language manual targeted at new human contributors:
index, overview, statuses & transitions (with mermaid state diagrams),
git workflow, MCP integration, docker, and troubleshooting. The manual
is the *map* — it cross-references existing runbooks/ADRs/architecture
docs rather than duplicating their content.
Regenerates docs/INDEX.md and validates with check-doc-links.mjs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(PBI-58): add markdown rendering deps + manual:build script
Adds mermaid, rehype-slug, rehype-autolink-headings for the in-app
/manual page. Wires manual:build into prebuild so production builds
always regenerate the chapter TOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-58): codegen script for in-app manual TOC
scripts/build-manual.mjs walks docs/manual/, parses YAML front-matter,
strips it from the body, and emits lib/manual.generated.ts with a typed
ManualEntry[] containing slug, title, description, filePath, and the
embedded markdown body. Pure Node 20, mirrors generate-docs-index.mjs.
Inlining the markdown at build time keeps runtime serverless functions
free of filesystem reads, which avoids whole-project NFT tracing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-58): /manual route renders developer manual chapters in-app
Catch-all route at app/(app)/manual/[[...slug]]/page.tsx with
generateStaticParams covering every TOC entry. Server-side
MarkdownView uses react-markdown with remark-gfm, rehype-slug, and
rehype-autolink-headings; mermaid code blocks are routed to a
client-only MermaidBlock that dynamic-imports mermaid on mount.
ManualSidebar (client) reads the typed TOC and highlights the active
chapter via usePathname.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-58): add Manual link to main nav bar
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ST-1272): allow PLAN_READY → GRILLING re-grill transition
actions/ideas.ts already lists PLAN_READY in GRILL_TRIGGERABLE_FROM,
but lib/idea-status.ts ALLOWED_TRANSITIONS was missing the
PLAN_READY → GRILLING edge. As a result, clicking Grill on a PLAN_READY
idea returned 422 "Status-transitie ongeldig" while the UI button was
enabled. Mirrors the existing PLANNED → GRILLING re-grill behaviour.
- lib/idea-status.ts: PLAN_READY allows GRILLING in addition to
PLANNING/PLANNED
- __tests__/lib/idea-status.test.ts: explicit assert for
PLAN_READY → GRILLING and PLAN_READY added to the regrill loop
covering every GRILL_TRIGGERABLE_FROM status
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-1275): render SKIPPED job status in chart-colors and insights
Closing the gap left when ClaudeJobStatus.SKIPPED was added to the schema:
the badge map and case-mapper already covered it, but the chart palette,
the per-day insights aggregator and the stacked-bar chart did not. SKIPPED
jobs (e.g. cmovkur8 manually flipped during the no-op-exit hotfix) now
render with a muted style consistent with cancelled.
- lib/chart-colors.ts: JOB_STATUS_COLORS gains a 'skipped' entry
(var(--muted-foreground), same intensity as cancelled — neither rood/orange)
- lib/insights/agent-throughput.ts: DayCount + STATUSES + perDay zero-fill
now include 'skipped'; the SQL terminal_7d filter already counted SKIPPED
- app/(app)/insights/components/agent-throughput.tsx: STACKED_STATUSES and
the empty-state guard include 'skipped'
- __tests__: chart-colors keys list, job-status round-trip ('all 7 statuses')
and the insights non-zero filter all account for SKIPPED
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-33): chat-kanaal UI — IdeaTimeline merge + UserChatInput
Voltooit de UI-laag van PLAN_CHAT (gebruikersvragen over plan, Claude
antwoordt async). Backend (UserQuestion model, createUserQuestionAction,
SSE-handling, server-side prop-passing) was al aanwezig — alleen de
UI-koppeling ontbrak waardoor userQuestions ongebruikt bleven.
- IdeaDetailLayout geeft userQuestions/planMd/ideaId/isDemo door aan
IdeaTimeline en telt user-questions mee in de tab-count
- IdeaTimeline mergt user-questions chronologisch met logs+questions,
rendert ze met MessageCircle-icoon en pending/answered status, en
toont onderaan UserChatInput wanneer plan_md aanwezig is
- UserChatInput nieuw component met textarea + verzend-knop dat
createUserQuestionAction aanroept en op success router.refresh()
triggert zodat SSE de pending-state oppikt
- useNotificationsRealtime: router toegevoegd aan useEffect-deps zodat
router.refresh() op user_question/idea-job events werkt zonder
stale-closure waarschuwing
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(lint): unused vars/imports + react-hook-form watch incompatibility
Resolves de overige lint-warnings van de gefaalde sprint-build die los
staan van PBI-33. Eslint-config staat unused vars/args toe als ze met
'_' prefixen, dus required interface-params krijgen een prefix terwijl
losse dode constantes/imports verwijderd worden.
- sprint-header: productId is required prop maar nog niet gebruikt
→ prefix _productId i.p.v. verwijderen (caller passeert het door)
- agent-throughput: STATUSES-constante was dood — verwijderd, queries
gebruiken hardcoded status-velden in de perDay-loop
- claude-jobs: productAccessFilter en enforceUserRateLimit waren
dode imports — verwijderd
- story-log.test: ongebruikte 'data' binding vervangen door bare
await res.json() zodat de stream nog wel geconsumeerd wordt
- product-dialog: form.watch('auto_pr') vervangen door useWatch met
control-prop — useWatch is veilig voor React Compiler memoization
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-cmovhveef): add PLANNED to GRILL_TRIGGERABLE_FROM and PLANNED→GRILLING transition
- GRILL_TRIGGERABLE_FROM now includes 'PLANNED' in actions/ideas.ts
- ALLOWED_TRANSITIONS PLANNED entry extended with 'GRILLING' in lib/idea-status.ts
- Updated canTransition test to reflect the new re-grill-from-PLANNED behavior
* test(ST-cmovhvef3): add exhaustive re-grill canTransition test covering PLANNED
Adds a loop test that asserts canTransition(status, 'GRILLING') for all
statuses in GRILL_TRIGGERABLE_FROM that support the transition, explicitly
documenting PLANNED as a valid re-grill entry point.
* feat(ST-cmovhvegf): add existingPbi pre-check in materializeIdeaPlanAction
- Adds options.allowAlongside parameter to control behaviour when a PBI
with executed tasks already exists.
- Returns 409 PBI_HAS_ACTIVE_TASKS:<code> when tasks are DONE/IN_PROGRESS
and allowAlongside is not set.
- Auto-deletes the old PBI inside the transaction when no tasks have been
executed (atomic replace).
- Alongside mode (allowAlongside=true) skips deletion and creates a new PBI.
* test(ST-cmovhveh3): add pre-check integration tests for materializeIdeaPlanAction
Three new scenarios in ideas-crud.test.ts:
- auto-vervang: old PBI deleted in transaction when no executed tasks
- conflict-409: returns PBI_HAS_ACTIVE_TASKS:<code> with active tasks
- alongside: skips delete and creates new PBI when allowAlongside=true
Also adds task.count, pbi.findUnique, pbi.delete to prisma mock.
* feat(ST-cmovhveih): remove PLANNED-blokkering in idea-row-actions, add inline Bekijk-PBI button
- Removed grillBlockedReason guard for status==='planned', enabling re-grill from PLANNED
- Removed the early return for PLANNED that hid all standard buttons
- Added conditional 'Bekijk <code>' button at the start of the standard button set,
visible only when status==='planned' and PBI + product_id are present
* feat(ST-cmovhvej7): add PBI_HAS_ACTIVE_TASKS alongside-dialoog in materialize handler
When materializeIdeaPlanAction returns code 409 with PBI_HAS_ACTIVE_TASKS:<code>,
a confirm dialog offers the user a choice: create new PBI alongside the existing one
or cancel. Alongside=true retries the action; cancel leaves the idea in PLAN_READY.
Scrum4Me-side counterpart of scrum4me-mcp@f7f5a48 (PBI-9 + PBI-47):
- prisma migration: ClaudeJob.{base_sha,head_sha} + SprintRun.pause_context
- lib/pause-context.ts: Zod schema + parsePauseContext + pauseReasonLabel
helper; single source of truth for the JSON pause_context shape produced
by the mcp sprint-run flow (MERGE_CONFLICT pause)
- actions/sprint-runs.ts: resumePausedSprintRunAction — separate from the
existing FAILED-resume flow, requires SprintRun.status === PAUSED, closes
the linked ClaudeQuestion, clears pause_context, sets RUNNING/QUEUED based
on whether a claim is still active
- components/sprint/sprint-run-controls.tsx: PAUSED banner with reason label,
PR link, conflict-files list (max 5 + "+N more"), Resume button with
confirm() guard
- app/(app)/products/[id]/sprint/page.tsx: load pause_context from active
SprintRun and pass through to SprintRunControls
All MD3 tokens (warning-container, on-warning-container, primary). No raw
Tailwind utility colours.
Tests: 532 passing across 72 files (Scrum4Me side).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ST-1243: F1 schema + propagateStatusUpwards-helper voor sprint-flow
Schema-uitbreidingen voor de sprint-niveau jobflow (PBI-46):
- TaskStatus, StoryStatus, PbiStatus, SprintStatus krijgen FAILED
- Nieuwe enums: SprintRunStatus, PrStrategy
- Nieuw SprintRun-model dat per-task ClaudeJobs groepeert
- ClaudeJob.sprint_run_id koppeling + index
- Product.pr_strategy (default SPRINT)
- Bijhorende Prisma-migratie
propagateStatusUpwards vervangt updateTaskStatusWithStoryPromotion en
herevalueert de keten Task → Story → PBI → Sprint → SprintRun bij elke
task-statuswijziging. Bij FAILED cancelt het sibling-jobs in dezelfde
SprintRun. PBI-status BLOCKED blijft handmatig en wordt niet overschreven.
Status-mappers + theme krijgen failed-token + label-uitbreidingen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ST-1244: F2 sprint-runs actions + deprecate per-task enqueues
actions/sprint-runs.ts (nieuw):
- startSprintRunAction met pre-flight (impl_plan / open ClaudeQuestion / PBI BLOCKED|FAILED)
- Maakt SprintRun + ClaudeJobs in PBI→Story→Task volgorde
- resumeSprintAction zet FAILED tasks/stories/PBIs terug en start nieuwe SprintRun
- cancelSprintRunAction breekt lopende SprintRun af zonder cascade
actions/claude-jobs.ts:
- enqueueClaudeJobAction, enqueueAllTodoJobsAction, previewEnqueueAllAction,
enqueueClaudeJobsBatchAction nu deprecation-stubs (UI-cleanup volgt in F4)
- cancelClaudeJobAction blijft beschikbaar voor losse jobs
Tests bijgewerkt: 11 nieuwe sprint-runs tests, claude-jobs(-batch) tests
herzien naar deprecation-asserties.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ST-1246: F4 UI Start/Resume/Cancel sprint + pr_strategy dropdown
- components/sprint/sprint-run-controls.tsx: knoppen Start Sprint
(sprintStatus=ACTIVE), Hervat sprint (sprintStatus=FAILED) en
Annuleer sprint-run (lopende run). Pre-flight blocker-modal toont
blockers met directe links naar de relevante pagina's.
- components/products/pr-strategy-select.tsx: dropdown SPRINT|STORY in
product-settings, met optimistic update + sonner-toast op fail.
- actions/products.ts: updatePrStrategyAction (eigenaar-only, demo-block).
- Sprint-page: query op actieve SprintRun + tonen van controls-balk.
Live cascade-visualisatie (T-634) staat als follow-up genoteerd —
huidige sprint-board statusbadges volstaan voor MVP. De Solo-board
"Voer uit"-knoppen zijn niet expliciet verwijderd; ze tonen nu de
deprecation-error van de gestubde actions tot de Solo-flow opnieuw
ontworpen wordt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Twee preventieve aanpassingen na de mislukte ST-2wj8mw8q-run, waarbij een
schema-edit (Todo-model verwijderd) groen door lint en vitest kwam maar
op `next build` brak vanwege 16 ongelinkte `prisma.todo`-references in 4
bestanden.
package.json:
- "typecheck": "tsc --noEmit" — losse script voor snelle full-project
type-check, los van eslint en next build.
- "verify": "npm run lint && npm run typecheck && npm test" — umbrella
voor agents/CI om voor de eind-build te valideren.
lib/idea-prompts/make-plan.md:
- Werkwijze-stap 3 toegevoegd: "Bij removal/refactor: doe een
dependency-cascade-grep". Voor de strikte format-sectie staat nu een
verplicht protocol:
- Removal van Prisma-model: grep `prisma.x` in actions/app/components/lib
- Removal van component/utility/type: grep paden + exports
- Hernoemen: per geraakt bestand een edit-taak
- Veld-wijziging in create/update: grep op die call-sites
- Eind-taak: `npm run typecheck` als sanity-check los van
lint/test/build.
- Toelichting waarom: eslint en vitest+esbuild slaan diepe type-check
over; next build is de eerste step die alles type-checkt en zit aan
het eind van de pijp.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drie functies via prisma.$queryRaw: getSprintTokenHistory (per-sprint
aggregaat), getDayTokenData (dag-totalen met guard op lege sprintId),
getPbiTokenAggregates (per-PBI met guard). Tests voor alle drie.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-vmc7vpps): lib/insights/token-stats.ts — sprint KPI + per-job query
SQL-queries voor totale tokens/kosten (KPI) en per-job tabel met
ModelPrice JOIN. Guard op lege sprintId. Tests voor empty guard,
KPI-mapping en null token-data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-vmc7vpps): TokenUsageCard — KPI-kaartjes + sorteerbare per-job tabel
Client-component met drie KPI-strips (totaal tokens, kosten USD, gem. per job)
en sorteerbare tabel op kosten of duur. Nulls als '—', MD3-tokens, geen
hardcoded kleuren.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-vmc7vpps): insights page — TokenUsageCard integreren
Voeg getTokenStats + TokenUsageCard imports toe aan insights/page.tsx.
tokenStats apart awaiten na activeSprints (kan niet in dezelfde Promise.all).
TokenUsageCard-sectie toegevoegd na AgentThroughputCard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Voegt IdeaProduct schema toe (dependency van story-qtkvz6ly), breidt
IdeaWithProduct type en IdeaDto interface uit met secondary_products array,
en laadt de relatie mee in findMany/findFirst in page.tsx en REST GET.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- lib/job-status.ts: SKIPPED ↔ 'skipped' mapping in beide richtingen
- components/shared/job-status.ts: label "Overgeslagen" + neutrale italic styling
- actions/admin/jobs.ts: cancel-guard erkent SKIPPED als eindstatus
- app/api/cron/cleanup-agent-artifacts: SKIPPED ook opruimen na 7d
- lib/insights/agent-throughput: SKIPPED telt mee als terminal
ACTIVE_JOB_STATUSES bewust ongewijzigd — SKIPPED is afgerond.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Nav-label 'Ideeën' hernoemd naar 'Ideas'; breadcrumb idem
- Grill/Plan tabs disabled (grijs, cursor-not-allowed) zolang er geen
content is; groene stip zodra grill_md resp. plan_md beschikbaar is
- SSE hook roept router.refresh() aan bij job done/failed zodat de
Timeline automatisch de nieuwe GRILL_RESULT/PLAN_RESULT logs toont
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- requireAdmin() checkt nu de database i.p.v. session.isAdmin (was altijd undefined)
- loginAction stelt session.isAdmin in op basis van UserRole in de DB
- registerAction stelt session.isAdmin = false expliciet in
- NavBar toont 'Admin'-link conditioneel als roles.includes('ADMIN')
- UserMenu ROLE_LABELS uitgebreid met ADMIN → 'Admin'
- Tests aangepast: prismaUserRole.findFirst mock toegevoegd
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Nieuwe server action in actions/user-questions.ts
- Aanmaken UserQuestion + ClaudeJob PLAN_CHAT in transactie
- Blokkeert als idea.plan_md null is of product ontbreekt
- Idempotency-check: geen dubbele PLAN_CHAT per idee
- pg_notify claude_job_enqueued event voor SSE-realtime
- Rate-limit config uitgebreid met create-user-question
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The /ideas/[id]?tab=timeline page is server-rendered: questions are a
prop snapshot. Without a router.refresh, new questions only show after a
manual page reload — and during a grill-session that's every ~20s.
Fix: in use-notifications-realtime, after dispatching idea-question
events to idea-store + re-syncing the bell, call \`router.refresh()\`.
This re-runs the server-component for whichever page the user is on; on
/ideas/[id] it pulls the new question. On other pages it's a no-op (no
visible state change).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two gaps discovered during the first live grill-session of IDEA-002:
the agent posted a question, but the user had no UI to answer it.
1. Idea-questions only appeared on the Timeline-tab as read-only entries
2. Notifications-bell fetched + handled story-questions only
This fix:
**Inline answer-form in IdeaTimeline** (components/ideas/idea-timeline.tsx)
- Open questions now render an AnswerForm directly under the question text
- Multi-choice options become clickable buttons (one-click submit); free-text
fallback via collapsed details/textarea
- Plain free-text questions render textarea + Verzend
- Calls existing answerQuestion server-action; toast + router.refresh on success
**Notifications-bell extended for idea-questions**
- stores/notifications-store.ts: NotificationQuestion → discriminated union
(kind: 'story' | 'idea'); forYouCount treats idea-questions as always-for-you
(idea is strictly user_id-only — only the owner sees them)
- components/notifications/notifications-bridge.tsx: parallel fetch of
story-questions (productAccessFilter) + idea-questions (idea.user_id ===
session.userId); merged + sorted by created_at
- components/notifications/notifications-sheet.tsx: renders idea_code/title
for kind='idea'
- components/notifications/answer-modal.tsx: header + open-link branch on
kind (idea → /ideas/[id]?tab=timeline; story → existing /sprint link)
- lib/realtime/use-notifications-realtime.ts: idea-question events also
trigger close+reconnect on 'open' (loads fresh detail) and remove(id) on
non-open — same pattern story-questions already use
- components/shared/notifications-bell.tsx: badge counts idea-questions as
for-you regardless of assignee
**Security gap closed (actions/questions.ts answerQuestion)**
Before: accepted any answer if user has product-access.
After: idea-questions require idea.user_id === session.userId; story-
questions keep the existing productAccessFilter path. (Prisma 7 rejects
\`{ not: null }\` in WHERE; routing happens app-level after a single fetch.)
Tests: 546/546 still green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- app/api/ideas/route.ts: GET (list with archived/product_id/status filters,
user_id-scope), POST (creates DRAFT with auto IDEA-NNN code, 201)
- app/api/ideas/[id]/route.ts: GET (idea + recent logs), PATCH
(ideaUpdateSchema, isIdeaEditable guard)
- lib/idea-dto.ts: API projection — converts Prisma row → DTO with
lowercase status + has_grill_md/has_plan_md flags (md content excluded
from list payloads, fetch via dedicated download action)
Auth: session OR API-token via authenticateApiRequest. Strict user_id
scope (no productAccessFilter — Idee is privé per Q8). 404 (not 403) for
foreign-user reads to prevent enumeration.
Tests: 13 cases (auth-401, demo-403, validation-422, malformed-400,
not-found-404, status-mismatch-422, filter param round-trip, DTO shape).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PrismaClientValidationError ('Argument \`not\` must not be null') hit at
runtime when notifications-bridge mounted post-M12 schema change.
Although StringNullableFilter typings allow \`not: null\`, the v7 query
engine rejects it.
Removed the WHERE-side filter in 3 places — null-narrowing already
happens client-side via flatMap / Boolean filter:
- components/notifications/notifications-bridge.tsx
- app/api/realtime/notifications/route.ts
- lib/insights/verify-stats.ts (task_id filter)
Idea-questions / idea-jobs will be routed via separate channels in
T-502 + T-507; for now, story-question + task-job paths simply ignore
NULL rows in their post-fetch mapping.
Tests: 479/479 green; tsc clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- new enums IdeaStatus, ClaudeJobKind, IdeaLogType
- new models Idea (with @@unique([user_id, code]) + pbi_id @unique) and IdeaLog
- User.idea_code_counter Int @default(0) for IDEA-{nnn} code generation
- ClaudeJob.task_id nullable; new idea_id + kind fields + index
- ClaudeQuestion.story_id nullable; new idea_id field + index
- existing call sites narrowed to story-questions / task-jobs (idea-paths come in T-502+)
- includes the M12 plan doc copied from /Users/janpetervisser/.claude/plans
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Story 5 van PBI "Alle dialogen conform docs/patterns/dialog.md".
- lib/schemas/sprint.ts — gedeelde zod-schemas (create/dates/goal)
- actions/sprints.ts — code+fieldErrors voor 422; code: 403 voor
auth/demo errors
- StartSprintButton dialog: useDirtyCloseGuard, useDialogSubmitShortcut,
entityDialog* layout-classes; DemoTooltip op trigger; veld-niveau
errors via fieldErrors
- SprintHeader's date- en complete-dialogen: zelfde behandeling; date-
dialog krijgt dirty-guard, complete-dialog krijgt DemoTooltip op
bevestigen
- docs/specs/dialogs/sprint.md — entity-profile dat alle drie de modes
documenteert; consolidatie naar één SprintDialog component bewust
uitgesteld
- Sprint-dates tests aangepast aan nieuwe action-shape
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vercel build was failing with TS18047 — `sprint.completed_at` is
possibly `null`. The earlier `.filter(s => s.completed_at != null)`
runtime-filtered the nulls out but did NOT narrow the element type;
TypeScript still saw `Date | null` on the result.
Add a user-defined type guard `(s): s is SprintWithCompletedAt =>` so
the narrowed array carries `completed_at: Date`. No runtime change.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>