* docs(cleanup): archief verouderde plannen, backlog en root-duplicaten
- 6 plans naar docs/old/plans/ (PBI-11/75/78, user-settings-store, Local github setup, lees-de-readme — laatste was verkeerde repo)
- docs/backlog/ naar docs/old/backlog/ (pre-MCP statische registry; live werk loopt via Scrum4Me-MCP)
- 6 root-level duplicaten naar docs/old/ (functional, {pbi,story,task}-dialog, product-backlog, backlog)
- 2 landing plans (niet uitgevoerd) krijgen archived: true frontmatter — blijven op plek maar uit INDEX
- scripts/generate-docs-index.mjs: skip docs/old/** + skip archived: true
- CLAUDE.md: rijen docs/backlog/, docs/plans/<key>-*.md, docs/manual/ weg; Track B-sectie verwijderd
- README.md / CHANGELOG.md / docs/plans/v1-readiness.md: link-fixes naar nieuwe locaties
Verify groen (lint + typecheck + 718 tests). docs/INDEX.md geregenereerd.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(cleanup): registreer handmatige verplaatsingen en fix referenties
- 4 plans verplaatst naar docs/old/plans/ (M10-qr-pairing-login, auto-pr-deploy-sync, docs-restructure-ai-lookup, v1-readiness)
- 3 archive-plans verplaatst naar docs/old/plans/ (archive-map nu leeg)
- ST-1114-copilot-reviews + 3 research-docs naar nieuwe docs/Ideas/ map
- Duplicaat docs/old/2026-04-27-m8-realtime-solo.md verwijderd (origineel zit in docs/old/plans/)
- Link-fixes naar nieuwe locaties:
- CHANGELOG.md → docs/old/plans/v1-readiness.md
- docs/runbooks/deploy-control.md → docs/old/plans/auto-pr-deploy-sync.md (2x)
- docs/runbooks/worker-idempotency.md → docs/old/plans/auto-pr-deploy-sync.md
- docs/plans/docs-restructure-pbi-spec.md → docs/old/plans/docs-restructure-ai-lookup.md (4x text + 2x href)
- docs/INDEX.md geregenereerd (96 docs, was 100)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
142 lines
7.1 KiB
Markdown
142 lines
7.1 KiB
Markdown
---
|
|
title: "ST-1114 — Copilot reviews op dashboard"
|
|
status: active
|
|
audience: [maintainer, contributor]
|
|
language: nl
|
|
last_updated: 2026-05-03
|
|
applies_to: [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
|
|
|
|
```prisma
|
|
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.
|
|
|
|
```bash
|
|
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)
|