- 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>
18 KiB
Plan — Auto-PR + selectieve deploy-controle + sync-zicht (end-to-end batch flow)
Bij merge: dit plan verplaatsen naar
docs/plans/auto-pr-deploy-sync.mdconform feedback-memory (plans indocs/plans/).
Context
Drie samenhangende problemen rond de "idee → uitvoeren"-keten:
- Worker stopt bij
commit. De Scrum4Me NAS-worker werkt lokaal: commits blijven op de machine staan totdat de gebruiker zelf pusht en een PR aanmaakt. Voor batch-uitvoer van story-jobs is dit een harde menselijke gate. - Deploy is alles-of-niets.
.github/workflows/ci.ymldeployt nu elke push naarmainautomatisch naar productie en elke PR naar preview.vercel.jsonheeft geengit.deploymentEnabled: false, dus Vercel's eigen Git-integratie deployt waarschijnlijk parallel mee → dubbele deploys en geen selectieve controle. - Geen zicht op voortgang per Idea/PBI. Concreet getest geval:
PBI-33 wordt nu de eerste sprint-batch — er is geen git-voetafdruk
(geen branch/commit/PR met "PBI-33"), geen activiteitenlog-entry,
en geen UI-pagina die per Story toont of er een ClaudeJob loopt, een
commit gepusht is, of een PR open/merged is. De data zit in
Story.status,ClaudeJob.pushed_at/branch/pr_url,Pbi.pr_url/pr_merged_at— er is alleen geen view die het joint.
Doel: de complete keten plan → job → commit → push → PR → auto-merge → deploy in één coherent ontwerp leggen, met (a) selectieve deploy-controle als veiligheidsklep en (b) een sync-tab die per Idea laat zien wat er werkelijk in git/PR-land gebeurd is.
Vastgelegde keuzes
Deploy-controle
- Mechanisme: PR-labels (B) + path-filter (C) gecombineerd.
- Eigenaar: GitHub Actions-workflow (A). Vercel Git-integratie uit.
- Defaults: PR → preview, push naar
main→ productie. - Override-richtingen:
skip-deploylabel: voorkomt preview-deploy op een PR.force-deploylabel: forceert deploy ook als path-filter doc-only zegt.
Auto-PR (uit IDEA-007-grill)
- Triggers in worker: na elke succesvolle
update_job_status('done')pusht de worker; na laatste story van een PBI maakt de worker een PR aan en activeert auto-merge (SQUASH). - Auth:
GITHUB_TOKENals omgevingsvariabele op de worker; geen UI of GitHub App in v1. - Foutafhandeling: push/PR-aanmaak-fail →
update_job_status('failed', error: …); geen force-push, geen automatische retry.
Interactie tussen beide
- Worker-PRs gebruiken hetzelfde labelsysteem als alle andere PRs.
Default = preview deploy, auto-merge wacht op CI groen, na merge
prod-deploy (mits path-filter zegt "code"). De worker zet geen
labels automatisch — als je batch-output zonder preview wilt mergen
moet je
skip-deployzelf toevoegen, of preview later uitzetten via een product-instelling (out-of-scope v1). - Implementatievolgorde: eerst deploy-controle (infra, onafhankelijk), daarna auto-PR (afhankelijk van stabiele deploy-flow).
Architectuur in één plaat
auto-merge wacht op
[story-job DONE] ─push branch─┐ deploy-preview groen
▼ │
[laatste story?]──ja──[PR + auto-merge]──CI──┴──merge naar main
│
[job: ci] altijd
│
[paths-filter]
│
├ PR → deploy-preview
│ if code && !skip-deploy
│ || force-deploy
│
└ push → deploy-production
if code
Deel A — Deploy-controle
A.1 vercel.json — Vercel Git-deploy uitzetten
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"git": { "deploymentEnabled": false },
"crons": [
{ "path": "/api/cron/expire-questions", "schedule": "0 4 * * *" },
{ "path": "/api/cron/cleanup-agent-artifacts", "schedule": "0 3 * * *" }
]
}
Effect: Vercel deployt niet meer automatisch op git-events. Alleen
vercel deploy vanuit de workflow (met VERCEL_TOKEN) maakt nog
deployments.
A.2 .github/workflows/ci.yml — path-filter + label-checks
Triggers uitbreiden met workflow_dispatch:
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
inputs:
target:
type: choice
description: Deploy target
options: [preview, production]
default: preview
Nieuwe job vóór de deploy-jobs:
changes:
name: Detect deploy-relevant changes
runs-on: ubuntu-latest
needs: ci
outputs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@v5
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
code:
- 'app/**'
- 'components/**'
- 'lib/**'
- 'actions/**'
- 'stores/**'
- 'prisma/**'
- 'public/**'
- 'package.json'
- 'package-lock.json'
- 'next.config.ts'
- 'tsconfig.json'
- 'vercel.json'
- 'proxy.ts'
- 'middleware.ts'
- '.github/workflows/**'
deploy-preview if-conditie aanpassen:
deploy-preview:
needs: [ci, changes]
if: |
github.event_name == 'pull_request' && (
(needs.changes.outputs.code == 'true'
&& !contains(github.event.pull_request.labels.*.name, 'skip-deploy'))
|| contains(github.event.pull_request.labels.*.name, 'force-deploy')
)
deploy-production if-conditie aanpassen:
deploy-production:
needs: [ci, changes]
if: |
github.ref == 'refs/heads/main'
&& github.event_name == 'push'
&& needs.changes.outputs.code == 'true'
Nieuwe deploy-manual job voor workflow_dispatch met inputs.target
→ vercel deploy of vercel deploy --prod.
A.3 GitHub-labels aanmaken
gh label create skip-deploy --color BFBFBF --description "Preview-deploy overslaan"
gh label create force-deploy --color 0E8A16 --description "Forceer deploy ondanks path-filter"
A.4 Documentatie
docs/runbooks/deploy-control.md — triggers, labels, path-filter,
voorbeelden. CLAUDE.md § Deployment-regel verwijst naar runbook.
Deel B — Auto-PR (worker → GitHub)
B.1 Acceptatiecriteria (uit IDEA-007)
- AC 1 — Push per story: Na succesvolle
update_job_status('done')pusht de worker via HTTPS (https://$GITHUB_TOKEN@github.com/…) naar origin. Push-timestamp via nieuwe MCP-call inClaudeJob.pushed_at. - AC 2 — Detectie laatste story: Nieuwe MCP-call
check_pbi_completeretourneert{ complete: boolean, pbi_id }. - AC 3 — PR aanmaken: Op
complete: truePOST naar/repos/{owner}/{repo}/pulls; titel/body uit PBI-naam + voltooide stories; PR-URL viaset_pbi_pr. - AC 4 — Auto-merge activeren: Direct na PR-aanmaak GraphQL
enablePullRequestAutoMerge(SQUASH). - AC 5 — Foutafhandeling: push/PR-fail →
update_job_status('failed', error); PR-URL blijft bewaard voor handmatige inspectie.
B.2 Server-side wijzigingen (Scrum4Me-repo)
Velden bestaan al in schema:
Product.auto_pr Boolean @default(false)(regel 176)Pbi.pr_url String?+Pbi.pr_merged_at DateTime?(regel 207–208)ClaudeJob.pushed_at DateTime?+ClaudeJob.pr_url String?+ClaudeJob.branch String?(regel 335, 338, 339)
Geen migratie nodig.
Server actions / REST: bestaande set_pbi_pr en mark_pbi_pr_merged
MCP-tools blijven. Nieuwe action:
actions/jobs.ts→recordJobPushedAtAction(jobId)voorpushed_at-write (als die nog niet via MCP gaat).
B.3 MCP-laag (scrum4me-mcp-repo)
Nieuwe tool:
check_pbi_complete(pbi_id) → { complete: boolean, pbi_id }. Leest alle ClaudeJobs gelinkt aan PBI; aggregeert status.complete = trueals alle story-jobs status DONE hebben.
Uitbreiding bestaande tool:
update_job_status: bijstatus: 'done'ookpushed_ataccepteren (worker geeft timestamp door).set_pbi_pr: ongewijzigd, bestaat al.
Schema-drift watchdog (docs/runbooks/mcp-integration.md) moet groen
voor merge.
B.4 Worker-laag (lokaal Claude-CLI worker)
Nieuwe stappen na elke story:
1. update_job_status('done', pushed_at: null) ← bestaand
2. git push https://$GITHUB_TOKEN@github.com/$OWNER/$REPO.git $BRANCH
3. record_pushed_at(job_id, now) ← nieuwe MCP-call
4. { complete } = check_pbi_complete(pbi_id)
5. if complete:
prNumber = POST /repos/.../pulls
set_pbi_pr(pbi_id, pr_url)
enablePullRequestAutoMerge(prNumber, MERGE_METHOD: SQUASH)
6. on any HTTP/git failure → update_job_status('failed', error)
GITHUB_TOKEN-scope: repo voor private, public_repo voor public.
Documenteer in worker-readme.
B.5 Repo-instellingen (handmatig, one-time)
- GitHub repo Settings → General → "Allow auto-merge" → aanvinken.
- Branch protection op
main: required CI checks =ci,deploy-previewis niet required (kan skipped zijn door label).
Deel C — Interactie & demo-policy
C.1 Interactie deploy-controle ↔ auto-PR
| Scenario | Preview-deploy | Prod-deploy bij merge |
|---|---|---|
| Worker maakt PR met code-changes (default) | ✅ runt | ✅ runt |
Worker maakt PR met skip-deploy (manueel toegevoegd) |
❌ skipped | ✅ runt |
| Worker maakt PR met enkel docs-changes (path-filter) | ❌ skipped | ❌ skipped |
User voegt force-deploy toe aan doc-only PR |
✅ runt | ✅ runt (path-filter) of ❌ (doc-only push) |
Auto-merge wacht op required CI checks. deploy-preview mag skipped
zijn — branch protection markeert hem niet als required.
C.2 Demo-policy
Auto-PR-flow draait op de worker, niet vanuit de webapp. Geen
demo-sessie kan deze code triggeren — geen extra proxy.ts of
session.isDemo-guards nodig. Wel: check_pbi_complete MCP-call moet
requireWriteAccess doen (consistent met andere write-MCP-tools), zodat
demo-tokens hem niet kunnen aanroepen.
Deel D — Sync-tab op Idea-detail (zicht op voortgang)
D.1 Wat bestaat al
model StoryLog(prisma/schema.prisma:251) met typesIMPLEMENTATION_PLAN | TEST_RESULT | COMMIT, pluscommit_hash,commit_message,metadata. Dit is de activiteitenlog.- MCP-tools
log_implementation,log_commit,log_test_resultschrijven naar deze tabel. - UI-component
components/shared/story-log.tsxrendertStoryLogEntry[]met type-styling. Story.status,ClaudeJob.pushed_at/branch/pr_url,Pbi.pr_url/pr_merged_atzijn al gevuld door bestaande flows.
Geen nieuwe tabellen, geen migraties.
D.2 Nieuwe tab op /ideas/[id]
Voeg vijfde tab Sync toe (naast Idee · Grill · Plan · Timeline) op
Idea-detail-page. Alleen zichtbaar als Idea.status === 'PLANNED' en
pbi_id gevuld.
Layout per tab-content:
- Header: PBI-link +
pr_url+pr_merged_atals badge. - Per Story (volgorde uit PBI): collapsible card met:
- Story-header: code · titel · status-badge.
- Job-rij: voor elke
ClaudeJob(kind=TASK_IMPLEMENTATION) gelinkt aan een Task van deze Story → status,branch,pushed_at,pr_url. Toont "geen job" als nog niets gequeued. - Activity-log:
<StoryLog logs={logs} repoUrl={product.repo_url} />— bestaande component, ongewijzigd.
D.3 Server-laag
Nieuwe loader in app/(app)/ideas/[id]/page.tsx (of nieuw
sync-tab-server.ts):
async function loadIdeaSyncData(ideaId: string, userId: string) {
// Auth-scope: idea.user_id === userId (M12-keuze 2)
return prisma.idea.findFirst({
where: { id: ideaId, user_id: userId },
include: {
pbi: {
include: {
stories: {
orderBy: { sort_order: 'asc' },
include: {
tasks: { include: { claude_jobs: true } },
logs: { orderBy: { created_at: 'desc' } },
},
},
},
},
},
})
}
Server-only. Nooit importeren in client component (zie hardstop
*-server.ts regel).
D.4 Realtime refresh
Sync-tab abonneert op bestaande SSE-streams:
app/api/realtime/solo/route.ts—JobPayloadvoor job-status-updates (al uitgebreid metkindenidea_idper Deel B).app/api/realtime/notifications/route.ts— voor StoryLog-inserts; als story_logs nog geen pg_notify-trigger heeft, voeg er een toe (nieuwe migratie, payload{op: 'INSERT', entity: 'story_log', id, story_id}).
Op event → router.refresh() of revalidate van Sync-tab data.
D.5 PBI-33 als live testgeval
PBI-33 is nu in TODO + gequeued als ClaudeJobs (gebruiker bevestigt: "taken op TODO gezet en claude-job aangemaakt"). Verwacht gedrag zodra deze sprint live is:
| Moment | Sync-tab toont |
|---|---|
| Job QUEUED | "Wachtend op worker" |
| Job RUNNING | Status RUNNING + log-entry IMPLEMENTATION_PLAN |
| Worker commit | log-entry COMMIT (hash + message) |
| Worker test | log-entry TEST_RESULT (status) |
| Worker push (Deel B AC 1) | branch + pushed_at zichtbaar |
| Laatste story → PR | PBI.pr_url zichtbaar |
| Auto-merge | PBI.pr_merged_at zichtbaar |
Als één van deze niet verschijnt: bug in MCP-tool of worker (niet in sync-tab zelf).
Bestanden
| Wijziging | Pad |
|---|---|
| Edit | vercel.json |
| Edit | .github/workflows/ci.yml |
| Nieuw | docs/runbooks/deploy-control.md |
| Edit | CLAUDE.md (verwijzing toevoegen) |
| Nieuw (mcp-repo) | src/tools/check-pbi-complete.ts |
| Edit (mcp-repo) | src/tools/update-job-status.ts (pushed_at) |
| Edit | actions/jobs.ts (optioneel: record-pushed-at) |
| Edit | Worker-script (post-story-hook + PR-aanmaak) |
| Doc | docs/runbooks/auto-pr-flow.md (worker-flow) |
| Nieuw | app/(app)/ideas/[id]/sync-tab-server.ts |
| Nieuw | components/ideas/idea-sync-tab.tsx |
| Edit | app/(app)/ideas/[id]/page.tsx (5e tab toevoegen) |
| Migratie | prisma/migrations/<ts>_story_logs_notify/migration.sql (pg_notify-trigger op story_logs) |
| Edit | app/api/realtime/notifications/route.ts (story_log-payload doorlaten) |
| GitHub (extern) | Labels skip-deploy, force-deploy aanmaken |
| GitHub (extern) | Repo Settings → "Allow auto-merge" aan |
| Vercel-dashboard | git.deploymentEnabled: false actief verifiëren |
Implementatievolgorde
-
Deel A — Deploy-controle
vercel.jsonaanpassenci.ymluitbreiden (path-filter, labels, dispatch)- Labels op GitHub aanmaken
- Runbook + CLAUDE.md-verwijzing
- Test-PR voor elk scenario (zie Verificatie)
-
Deel D — Sync-tab (kan parallel met B; alleen DB-reads + UI)
loadIdeaSyncDataserver-loaderidea-sync-tab.tsxcomponent met<StoryLog>-hergebruik- 5e tab in
app/(app)/ideas/[id]/page.tsx - pg_notify-trigger op
story_logs+ SSE-route uitbreiden - Live test op PBI-33 (sprint loopt al — check of activity verschijnt zodra worker logs schrijft)
-
Deel B — Auto-PR
- MCP
check_pbi_complete+update_job_status(pushed_at)PR (parallel-repo, schema-drift-watchdog groen) - Worker-hook: push na done, PR + auto-merge bij complete
- Repo-instelling "Allow auto-merge" aan
- End-to-end smoke met één test-PBI
- MCP
Verificatie
Lokaal:
npm run lint && npm test && npm run build
Workflow-syntax:
gh workflow view ci.yml
End-to-end deploy-controle:
- Doc-only PR →
deploy-previewskipped. - Doc-only PR +
force-deploy→deploy-previewrunt. - Code-PR +
skip-deploy→deploy-previewskipped. - Code-PR zonder labels →
deploy-previewrunt. - Push naar
mainmet code-change →deploy-productionrunt. - Push naar
maindoc-only →deploy-productionskipped. workflow_dispatchtarget=production → manuele prod.- Vercel dashboard → geen auto-deploy bij geforceerde test-push.
End-to-end auto-PR:
- Maak een test-PBI met 1 story + 1 task.
- Worker draait → na
done:pushed_atgevuld, branch op origin zichtbaar. check_pbi_complete→complete: true.- PR verschijnt op GitHub met titel = PBI-naam, body = story-list.
- Auto-merge actief; CI groen → squash-merge.
mark_pbi_pr_mergedgetriggerd doorpull_request: closed-webhook (al bestaand) →Pbi.pr_merged_atgevuld.- Push-event op
main→deploy-productionrunt (path-filter ja). - Failure-test: revoke GITHUB_TOKEN tijdelijk → push faalt →
update_job_status('failed')met error; geen PR aangemaakt.
Out-of-scope (v1)
- UI-toggle voor
auto_prper product (veld bestaat, geen UI-wiring). - GitHub App-installatie (per-repo tokens, scopes-finetuning).
- Multi-repo PBI's (huidig ontwerp: één
repo_urlper PBI). - Force-push / non-fast-forward retry-flow.
- Notificaties (Slack, e-mail) bij merge of CI-failure.
- Rollback-flow bij gemergende regressie.
- Migratie naar
vercel.ts(knowledge-update beveelt het aan; later). - Auto-skip preview-deploy specifiek voor worker-PRs op basis van product-instelling.