feat(PBI-67): IDEA_REVIEW_PLAN Phases 3-6 — server actions, UI components, prompt & tests

- Phase 3: startReviewPlanJobAction, cancelIdeaJobAction, status transitions
  (REVIEWING_PLAN / PLAN_REVIEWED / PLAN_REVIEW_FAILED), status colors,
  job-card/jobs-column filters, idea-list status tabs
- Phase 4: review-plan-job.md prompt (multi-model orchestration with codex
  injection + active plan revision via update_idea_plan_md after each round),
  runbook, 13 unit tests
- Phase 5: ReviewLogViewer component (rounds, convergence, approval, issues),
  idea-detail integration, proper ReviewLog TypeScript types exported from component
- Phase 6.1: wait-for-job discriminator wired (IDEA_REVIEW_PLAN), plan-revision
  step made mandatory in prompt (was previously optional/missing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-14 03:33:44 +02:00
parent 873b42a87e
commit dac890b82c
18 changed files with 1952 additions and 13 deletions

View file

@ -8,6 +8,7 @@ import { productAccessFilter } from '@/lib/product-access'
import { ideaToDto } from '@/lib/idea-dto'
import { IdeaDetailLayout } from '@/components/ideas/idea-detail-layout'
import { loadIdeaSyncData } from './sync-tab-server'
import type { ReviewLog } from '@/components/ideas/review-log-viewer'
export const dynamic = 'force-dynamic'
@ -26,10 +27,25 @@ export default async function IdeaDetailPage({ params, searchParams }: PageProps
// M12: strikt user_id-only — 404 (niet 403) voor andere users (anti-enum).
const idea = await prisma.idea.findFirst({
where: { id, user_id: session.userId },
include: {
select: {
id: true,
user_id: true,
product_id: true,
code: true,
title: true,
description: true,
status: true,
pbi_id: true,
archived: true,
grill_md: true,
plan_md: true,
plan_review_log: true,
reviewed_at: true,
created_at: true,
updated_at: true,
product: { select: { id: true, name: true, repo_url: true } },
pbi: { select: { id: true, code: true, title: true } },
secondary_products: { include: { product: { select: { id: true, name: true } } } },
secondary_products: { select: { id: true, product_id: true, product: { select: { id: true, name: true } } } },
},
})
if (!idea) notFound()
@ -91,6 +107,7 @@ export default async function IdeaDetailPage({ params, searchParams }: PageProps
idea={ideaToDto(idea)}
grill_md={idea.grill_md}
plan_md={idea.plan_md}
plan_review_log={(idea.plan_review_log as ReviewLog | null) ?? null}
products={products}
logs={logs.map((l) => ({
id: l.id,