* 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>
7.1 KiB
| title | status | audience | language | last_updated | applies_to | |||
|---|---|---|---|---|---|---|---|---|
| ST-1114 — Copilot reviews op dashboard | active |
|
nl | 2026-05-03 |
|
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:
GET /api/products(Bearer-auth) — lijst toegankelijke producten metrepo_url- Per product: parse
owner/repouitrepo_url,octokit.pulls.list({state: 'all', per_page: 50}) - Per PR:
octokit.pulls.listReviews(...), filter opuser.type === 'Bot' && user.login.includes('copilot') POST /api/copilot-reviewsmet{ product_id, reviews: [...] }— endpoint doetdeleteMany+createManyper product (atomic replace)- 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
localStoragekeyed opreview.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:
- PR-state filter: alle PR's of alleen
state=open? (closed-PRs hebben oude reviews die misschien niet meer relevant zijn) - Markdown-rendering: react-markdown, of plain
<pre>? (react-markdown is +35KB bundle) - 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)
npm run devGITHUB_TOKEN=... SCRUM4ME_API_TOKEN=... npx tsx scripts/sync-copilot-reviews.ts— toontn reviews opgeslagen- Browser refresht dashboard → "Copilot reviews"-sectie toont cards met PR-titels
- Klik "Implementeer" → kaart krijgt groen randje, decision in localStorage
- Refresh → state blijft (localStorage)
- Filter toggle "Alleen te beoordelen" → cards met decision verdwijnen
- Demo-user: kan reviews zien, maar
POST /api/copilot-reviewsweigert (al via middleware-guard van ST-1110)