Scrum4Me/docs/plans/ST-1114-copilot-reviews.md

7.1 KiB

title status audience language last_updated applies_to
ST-1114 — Copilot reviews op dashboard active
maintainer
contributor
nl 2026-05-03
ST-1114

Plan — ST-1114 · Copilot reviews op dashboard

Context

Als ontwerper wil je een overzicht zien van GitHub Copilot's PR-reviews om per stuk te beslissen of je 'm implementeert of overslaat. De codebase heeft nu nul GitHub-integratie — alleen product.repo_url als string voor hyperlinks. We bouwen een minimale, hobby-vriendelijke architectuur.

Architectuurkeuzes (via AskUserQuestion bevestigd)

  • Auth: lokaal script met GITHUB_TOKEN — webapp heeft GEEN GitHub-credentials. Het script draai je lokaal wanneer je wil verversen.
  • Fetch: on-demand op dashboard-load (server-side prisma.copilotReview.findMany, geen externe call)
  • Decision: alleen visuele toggle in localStorage (geen DB-persistentie)
  • Scope: MVP — tonen + lokale toggle. Geen cron, geen webhook, geen GitHub-auth in productie.

Architectuur

┌──────────────┐   octokit   ┌────────────┐   API token   ┌─────────────┐
│ scripts/     │ ──────────▶ │  GitHub    │               │  Scrum4Me   │
│ sync-copilot │             │  REST API  │               │  /api/      │
│ -reviews.ts  │ ◀────────── │            │               │  copilot-   │
└──────────────┘   reviews   └────────────┘   POST batch  │  reviews    │
        │                                                  │             │
        └──────────────────────────────────────────────────▶ DB upsert  │
                                                            └──────┬──────┘
                                                                   │
                                                            ┌──────▼──────┐
                                                            │  /dashboard │
                                                            │ server-side │
                                                            │ findMany    │
                                                            └─────────────┘

Het script is de enige plek waar GitHub-credentials nodig zijn. Productie kent alleen Scrum4Me-data.

Datamodel

model CopilotReview {
  id            String   @id @default(cuid())
  product       Product  @relation(fields: [product_id], references: [id], onDelete: Cascade)
  product_id    String
  pr_number     Int
  pr_title      String
  pr_url        String
  pr_state      String   // 'open' | 'closed' | 'merged'
  author_login  String   // bv. 'copilot-pull-request-reviewer[bot]'
  review_state  String   // 'COMMENTED' | 'APPROVED' | 'CHANGES_REQUESTED'
  body          String   @db.Text
  submitted_at  DateTime
  html_url      String   // directe link naar de review
  fetched_at    DateTime @default(now())

  @@unique([product_id, pr_number, submitted_at])
  @@index([product_id, submitted_at])
  @@map("copilot_reviews")
}

@@unique zorgt voor idempotency — script kan herhaald draaien zonder dupes. Geen decision-veld: dat staat in localStorage.

Script

scripts/sync-copilot-reviews.ts — TypeScript via tsx, leest env, gebruikt Octokit, POST naar eigen API.

GITHUB_TOKEN=ghp_... \
SCRUM4ME_API_URL=http://localhost:3000 \
SCRUM4ME_API_TOKEN=s4m_... \
npx tsx scripts/sync-copilot-reviews.ts

Stappen:

  1. GET /api/products (Bearer-auth) — lijst toegankelijke producten met repo_url
  2. Per product: parse owner/repo uit repo_url, octokit.pulls.list({state: 'all', per_page: 50})
  3. Per PR: octokit.pulls.listReviews(...), filter op user.type === 'Bot' && user.login.includes('copilot')
  4. POST /api/copilot-reviews met { product_id, reviews: [...] } — endpoint doet deleteMany + createMany per product (atomic replace)
  5. Print samenvatting: aantal reviews per product + totale runtime

API endpoint

app/api/copilot-reviews/route.ts:

  • POST: Bearer-auth, demo-block, payload { product_id, reviews: CopilotReview[] }. Atomic transaction: delete-all-for-product → createMany. Validatie via Zod.
  • GET: niet nodig — dashboard leest direct via Prisma server-side. Endpoint kan komen voor toekomstige clients.

Dashboard widget

Boven of onder de bestaande product-grid een nieuwe sectie "Copilot reviews".

components/dashboard/copilot-reviews.tsx (client component):

  • Props: reviews: CopilotReview[] (server-fetched)
  • Lijst met cards: PR-titel + nummer (link naar PR), Copilot's body (truncated of accordion), state-badge, "Implementeer" / "Skip"-knoppen
  • Decision-state in localStorage keyed op review.id: 'implement' | 'skip' | undefined (default: ongezien)
  • Cards met decision='skip' visueel gedimmed; cards met 'implement' krijgen een groen randje
  • Filter-toggles bovenaan: "Alle / Te beoordelen / Implementeren / Skip"
  • Empty state: "Geen Copilot-reviews gevonden — draai het sync-script."

app/(app)/dashboard/page.tsx past prisma.copilotReview.findMany({ where: { product_id: { in: accessibleIds } }, orderBy: { submitted_at: 'desc' } }) en geeft door.

Voorgestelde sub-tasks

Code Onderwerp
ST-1114.2 DB: CopilotReview model + migration
ST-1114.3 API: POST /api/copilot-reviews (Bearer-auth + demo-block + replace-by-product)
ST-1114.4 Script: scripts/sync-copilot-reviews.ts met octokit
ST-1114.5 UI: dashboard-widget met cards, localStorage-decision, filter-toggle
ST-1114.6 Tests: API endpoint (auth, demo-block, validation), dashboard-widget snapshot
ST-1114.7 Docs: README-sectie over script + env-vars; CLAUDE.md-update

M11-keuzes voor de implementerende sessie

Drie open beslissingen die niet kritiek zijn voor het plan zelf:

  1. PR-state filter: alle PR's of alleen state=open? (closed-PRs hebben oude reviews die misschien niet meer relevant zijn)
  2. Markdown-rendering: react-markdown, of plain <pre>? (react-markdown is +35KB bundle)
  3. localStorage-key-vorm: scrum4me:copilot-decision:{review_id} per review, of één map-object onder één key?

Branch + PR

  • Branch: feat/M14-copilot-reviews (M14 = nieuwe milestone)
  • 6 commits (.2 t/m .7), één per laag
  • PR pas openen na handmatige test door gebruiker

Verificatie (end-to-end)

  1. npm run dev
  2. GITHUB_TOKEN=... SCRUM4ME_API_TOKEN=... npx tsx scripts/sync-copilot-reviews.ts — toont n reviews opgeslagen
  3. Browser refresht dashboard → "Copilot reviews"-sectie toont cards met PR-titels
  4. Klik "Implementeer" → kaart krijgt groen randje, decision in localStorage
  5. Refresh → state blijft (localStorage)
  6. Filter toggle "Alleen te beoordelen" → cards met decision verdwijnen
  7. Demo-user: kan reviews zien, maar POST /api/copilot-reviews weigert (al via middleware-guard van ST-1110)