Scrum4Me/docs/plans/M8-bootstrap-wizard-upload.md
Janpeter Visser d84cdf664f
feat(PBI-67): IDEA_REVIEW_PLAN — iterative multi-model plan review (#199)
* feat(ideas): upload-plan knop — short-circuit van Make-Plan AI-flow

Voegt een 'Upload plan' knop toe in idea-row-actions (verschijnt in zowel
list als idea-detail). Klik → file picker → kies .md → server-side parse +
opslaan; idea-status springt naar PLAN_READY. Vandaaruit de bestaande
'Maak PBI' knop voor materialize.

Server (uploadPlanMdAction):
- Toegestaan vanuit DRAFT, GRILLED, PLAN_FAILED, PLAN_READY
- DRAFT → skip-grill: status gaat direct naar PLAN_READY
- PLAN_READY overschrijft het bestaande plan (consistent met
  updatePlanMdAction, geen confirmation)
- Geblokkeerd in GRILLING/PLANNING (job loopt), PLANNED (al gematerialiseerd)
- Parse-failure → 422 + details (NIET opslaan, zodat een onparseerbaar plan
  nooit in de DB belandt)
- Empty / >100k chars → 422
- Schrijft IdeaLog NOTE met from_status + length
- Rate-limit + demo-guard + ownership-check via loadOwnedIdea (zelfde
  patroon als updatePlanMdAction)

UI (idea-row-actions.tsx):
- Hidden <input type=file accept=".md,.markdown,text/markdown,text/plain">
- FileReader → text → action
- Toast bij success + router.refresh()
- Blocked-tooltip in andere statussen

Tests: 10 nieuwe in __tests__/actions/ideas-crud.test.ts dekkend voor:
happy paths (DRAFT/GRILLED/PLAN_READY-overwrite/PLAN_FAILED), blocks
(PLANNED/GRILLING), validation (empty/oversized/parse-fail), 404.
Full suite groen: 849/849.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add reviews for Bootstrap-wizard plans v3.2 to v3.4

- Review v3.2: Addressed executor model, fire-and-forget issues, and PAT handling.
- Review v3.3: Improved transaction handling, stale recovery, and ID generation.
- Review v3.4: Finalized GitHub permissions, catalog versioning, and E2E verification queries.
- Updated recommendations for each version to enhance implementation readiness.

* docs(plans): M8 bootstrap-wizard upload-variant v1.4 — backtick-paden

Upload-variant van het volledige technische plan (docs/plans/M8-bootstrap-wizard.md),
bedoeld voor de "Upload plan"-functie. Genereert 1 PBI + 4 Stories + 22 Tasks
via materializeIdeaPlanAction.

v1.4-aanpassingen tov eerdere generatie-iteratie:
- Alle bestandspaden in implementation_plan in backticks (path-extractor matchen)
- Expliciete "Bestanden:" blok per task vóór de stappen
- Alle tasks op verify_required: ALIGNED_OR_PARTIAL (was deels ALIGNED — te strict
  voor ADR-stubs en multi-file edits)

Fixt forward-only: T-963 cancelled_by_self door DIVERGENT verifier-verdict.
Re-upload van dit bestand produceert tasks die door verify_task_against_plan
als ALIGNED of PARTIAL geclassificeerd kunnen worden.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* PBI-67: Add review-plan support to Idea model and job config

- Add plan_review_log and reviewed_at fields to Idea model
- Add REVIEWING_PLAN, PLAN_REVIEW_FAILED, PLAN_REVIEWED to IdeaStatus enum
- Add IDEA_REVIEW_PLAN to ClaudeJobKind enum
- Add IDEA_REVIEW_PLAN config to job-config.ts with model=opus, thinking_budget=6000
- Create migration record for schema changes (applied via db push)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* PBI-67 Phase 2: Add update-idea-plan-reviewed MCP tool

- Create src/tools/update-idea-plan-reviewed.ts: saves review-log and transitions idea status to PLAN_REVIEWED
- Add PLAN_REVIEW_RESULT to IdeaLogType enum (both repos)
- Register tool in src/index.ts
- Update Prisma schemas (both repos): add plan_review_log and reviewed_at fields to Idea model
- Add REVIEWING_PLAN, PLAN_REVIEW_FAILED, PLAN_REVIEWED to IdeaStatus enum (MCP schema)
- Add IDEA_REVIEW_PLAN to ClaudeJobKind enum (MCP schema)
- Tool includes transaction safety and convergence metrics logging

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* 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>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 03:35:02 +02:00

34 KiB

pbi stories
title description priority
Bootstrap-wizard voor nieuwe Product-repo Bij het aanmaken van een nieuw Product wil de user direct een GitHub-repo bootstrappen volgens canonical conventies (MD3-theme, ADR-systeem, docs-structuur, tooling). De catalogus van aanvinkbare opties + uitvoer- recepten leeft in de database (configureerbaar, audit-bar). Uitvoering gebeurt server-side via een aparte `bootstrap-service` — een deterministic runtime onder ClaudeJobKind `BOOTSTRAP_REPO`. UX: twee-staps (Product eerst, wizard later) met Configure → Preview → Run. Volledig technisch plan: docs/plans/M8-bootstrap-wizard.md (v3.5). 2
title description acceptance_criteria priority tasks
Sprint 1a — Deterministic-job contracten + drift-CI Leg de fundamentele contracten vast voordat schema/UI/service worden gebouwd: discriminated-union JobConfig, docker-runner skip-filter, transactionele status-sync helper, shared bootstrap-actions package scaffold, en vendor-copy drift-detectie via CI hash-check. - ADR-0009 in docs/adr/ met status accepted - JobConfig is een discriminated union; BOOTSTRAP_REPO → runtime:'deterministic' - scrum4me-docker claimt geen BOOTSTRAP_REPO-jobs (skip-filter actief) - packages/bootstrap-actions/ scaffold bestaat in Scrum4Me-repo - notify-helper doet post-commit pg_notify (NOTIFY niet in transaction) - check-bootstrap-actions-hash.sh faalt CI bij drift 1
title description implementation_plan priority verify_required
Schrijf ADR-0009 voor bootstrap-wizard architectuur Nygard-template ADR die de architectuur-keuze vastlegt: aparte bootstrap-service als sibling-directory, deterministic runtime, PAT-secret-boundary, declarative recipes in DB. Bestanden: - `docs/adr/0009-bootstrap-wizard.md` (nieuw) - `docs/adr/README.md` (update) - `docs/INDEX.md` (regenereer) Stappen: 1. Maak `docs/adr/0009-bootstrap-wizard.md` op basis van `docs/adr/templates/nygard.md` 2. Sectie Context: waarom deze feature; verwijs naar `docs/plans/M8-bootstrap-wizard.md` 3. Sectie Decision: bootstrap-service als sibling; ClaudeJob queue hergebruikt; declarative actions 4. Sectie Consequences: positive (consistent product-onboarding), negative (extra service om te beheren) 5. Status: accepted 6. Update `docs/adr/README.md` met nieuwe ADR-link 7. Regenereer `docs/INDEX.md` via `npm run docs` 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Implementeer JobConfig discriminated union Vervang het bestaande JobConfig-type door een discriminated union met `runtime: 'claude' | 'deterministic'`. BOOTSTRAP_REPO returnt `{ runtime: 'deterministic', executor: 'bootstrap-repo' }` zonder model/thinking_budget/permission_mode. Bestanden: - `lib/job-config.ts` - `scrum4me-mcp/src/lib/job-config.ts` (gespiegeld) - `__tests__/lib/job-config.test.ts` (nieuw) Stappen: 1. Refactor `lib/job-config.ts` naar discriminated union (runtime-discriminator) 2. KIND_DEFAULTS toevoegen: BOOTSTRAP_REPO → deterministic 3. resolveJobConfig() returnt union; consumers krijgen exhaustive switch 4. getJobConfigSnapshot() schrijft requested_* als null voor deterministic kinds 5. Spiegel `scrum4me-mcp/src/lib/job-config.ts` identiek (geen drift) 6. Tests: BOOTSTRAP_REPO → runtime='deterministic'; alle bestaande kinds → runtime='claude' 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
scrum4me-docker skip-filter voor BOOTSTRAP_REPO De docker-runner mag geen BOOTSTRAP_REPO-jobs claimen — die zijn voor de aparte bootstrap-service. Voeg kind-filter toe aan tryClaimJob. Bestanden: - `scrum4me-docker/bin/run-one-job.ts` - `scrum4me-docker/README.md` (note over filter) Stappen: 1. Open `scrum4me-docker/bin/run-one-job.ts` 2. In tryClaimJob SQL: voeg `AND kind <> 'BOOTSTRAP_REPO'` toe aan WHERE 3. Test: enqueue BOOTSTRAP_REPO-job; verifieer dat docker-runner het overslaat 4. Update `scrum4me-docker/README.md` met note over kind-filter 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Scaffold packages/bootstrap-actions/ shared package Nieuw package binnen Scrum4Me-repo dat schema + handler-interfaces bevat. Geen secrets; gedeeld tussen app (dry-run) en service (echte run via vendor-copy). Bestanden: - `packages/bootstrap-actions/package.json` (nieuw) - `packages/bootstrap-actions/tsconfig.json` (nieuw) - `packages/bootstrap-actions/src/types.ts` (nieuw) - `packages/bootstrap-actions/src/schema.ts` (nieuw) - `packages/bootstrap-actions/src/index.ts` (nieuw) - `tsconfig.json` (path-alias toevoegen) Stappen: 1. Maak directory `packages/bootstrap-actions/src/` 2. `packages/bootstrap-actions/package.json` met name "@scrum4me/bootstrap-actions" version 0.1.0 3. `packages/bootstrap-actions/tsconfig.json` extending root config 4. `packages/bootstrap-actions/src/types.ts`: ActionContext, DryRunReport, CatalogSnapshot, RecipeSnapshot interfaces 5. `packages/bootstrap-actions/src/schema.ts`: skelet ActionSchema (lege discriminated union; uitgewerkt in story 2) 6. `packages/bootstrap-actions/src/index.ts`: re-exports 7. `tsconfig.json` path-alias `@scrum4me/bootstrap-actions` → `./packages/bootstrap-actions/src` 8. Verifieer build: `npm run typecheck` slaagt 2 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
lib/bootstrap/notify.ts post-commit pg_notify helper Helper voor transactionele status-updates met NOTIFY ná commit (niet IN transaction). Payload-contract: type='claude_job_status', user_id verplicht, kind, status (lowercase via jobStatusToApi), bootstrap_run_id. Bestanden: - `lib/bootstrap/notify.ts` (nieuw) - `__tests__/lib/bootstrap/notify.test.ts` (nieuw) Stappen: 1. Maak `lib/bootstrap/notify.ts` 2. Functie notifyClaudeJobStatus(jobId, userId, status, extra?) die pg_notify('scrum4me_changes', payload) 3. status wordt door jobStatusToApi() naar lowercase 4. Wrapper-functie withPostCommitNotify(tx, payload) die NOTIFY ná tx commit doet 5. Unit-tests in `__tests__/lib/bootstrap/notify.test.ts`: NOTIFY niet aangeroepen bij rollback; wel bij commit; payload-shape klopt 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Schema-hash drift CI-check script Voorkomt drift tussen Scrum4Me/packages/bootstrap-actions en de vendor-copy in bootstrap-service. Hash-vergelijking faalt CI. Bestanden: - `scripts/check-bootstrap-actions-hash.sh` (nieuw) - `.github/workflows/ci.yml` (CI-job toevoegen) - `docs/runbooks/bootstrap-wizard.md` (placeholder, uitgewerkt sprint 1d) Stappen: 1. Maak `scripts/check-bootstrap-actions-hash.sh` 2. Script berekent sha256 over `packages/bootstrap-actions/src/**` 3. Schrijf hash naar `packages/bootstrap-actions/.schema-hash` bij build 4. CI-job in `.github/workflows/ci.yml`: vergelijk geschreven hash met bron-hash; faal bij mismatch 5. Documenteer in `docs/runbooks/bootstrap-wizard.md` 2 ALIGNED_OR_PARTIAL
title description acceptance_criteria priority tasks
Sprint 1b — Schema + seed + path-safety Volledige Prisma-modellen voor catalog (Category/Option/Action), BootstrapRun met side-effect checkpoints, Product/User uitbreidingen, partial unique index voor "1 active run per product". Plus seed met alle 6 core categorieën en Zod-validatie per action-kind. - npx prisma migrate dev slaagt - npm run seed produceert 7 categorieën (6 core SINGLE + 1 addons MULTI) - Partial unique index "bootstrap_runs_one_active_per_product" bestaat - Action-Zod schema rejected path-traversal en absolute paden - Jobs-board (job-card/jobs-column) toont BOOTSTRAP_REPO label - npm run typecheck groen na enum-uitbreiding 1
title description implementation_plan priority verify_required
Prisma-modellen + migration BootstrapCategory/Option/Action/Run + enums (BootstrapSelectionType, BootstrapActionKind, BootstrapRunStatus, RiskLevel, RoleRequired, SideEffect) + Product/User uitbreidingen. Snake-case via @@map. Bestanden: - `prisma/schema.prisma` - `prisma/migrations/<ts>_bootstrap_wizard/migration.sql` (Prisma genereert + manual append) - `scrum4me-mcp/prisma/schema.prisma` (gesynced via `sync-schema.sh`) Stappen: 1. Open `prisma/schema.prisma` 2. Voeg modellen toe: BootstrapCategory, BootstrapOption, BootstrapAction, BootstrapRun met @@map(snake_case) 3. Enums: BootstrapSelectionType (SINGLE|MULTI), BootstrapActionKind, BootstrapRunStatus (incl. FAILED_NEEDS_CLEANUP), RiskLevel, RoleRequired, SideEffect 4. Product: voeg repo_owner, repo_slug, template_version, last_bootstrap_run_id velden + @@unique([repo_owner, repo_slug]) + relaties met disjoint names (ProductBootstrapRuns history + ProductLastBootstrapRun pointer) 5. User: voeg github_pat_encrypted, github_username, github_pat_verified_at, github_pat_scopes (@default([])), github_pat_expires_at, github_orgs velden 6. ClaudeJob: voeg claimed_by_worker_id en bootstrap_run relation. ClaudeJobKind enum: BOOTSTRAP_REPO erbij 7. BootstrapRun met @unique claude_job_id, github_repo_created_at/id/full_name, push_completed_at, recipe_hash, catalog_version, action_schema_version, dry_run_report 8. Indexes: bootstrap_runs (product_id, status), (user_id, created_at), (status, finished_at) 9. `npx prisma migrate dev --name bootstrap_wizard` 10. Append raw SQL aan migration: `CREATE UNIQUE INDEX bootstrap_runs_one_active_per_product ON bootstrap_runs (product_id) WHERE status IN ('PENDING','RUNNING')` 11. Sync schema naar `scrum4me-mcp/prisma/schema.prisma` via `sync-schema.sh` 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Action-handlers + Zod-schema in shared package Per BootstrapActionKind een handler-functie + Zod-validatie. Path-safety regels (deny .git, absolute paths, traversal); run-level caps (200 acties, 256KiB log). Bestanden: - `packages/bootstrap-actions/src/schema.ts` (uitbreiden) - `packages/bootstrap-actions/src/handlers/copy-file.ts` - `packages/bootstrap-actions/src/handlers/write-file.ts` - `packages/bootstrap-actions/src/handlers/append-to-file.ts` - `packages/bootstrap-actions/src/handlers/replace-string.ts` - `packages/bootstrap-actions/src/handlers/create-adr-stub.ts` - `packages/bootstrap-actions/src/handlers/add-dependency.ts` - `packages/bootstrap-actions/src/recipe-hash.ts` - `packages/bootstrap-actions/src/catalog-hash.ts` - `packages/bootstrap-actions/src/__tests__/*.test.ts` Stappen: 1. `packages/bootstrap-actions/src/schema.ts`: discriminated union met SafeRelPath validator 2. SafeRelPath: max 256, regex [A-Za-z0-9_./-], deny absolute/'..'/'.git' 3. Handlers: COPY_FILE, WRITE_FILE, APPEND_TO_FILE, REPLACE_STRING, CREATE_ADR_STUB, ADD_DEPENDENCY (regex docs MVP-beperking: alleen exact/range semver) 4. RUN_BASH_TEMPLATE met allowlist (commented out in MVP — opt-in via fase-2) 5. `packages/bootstrap-actions/src/recipe-hash.ts`: canonicalize() + sha256 6. `packages/bootstrap-actions/src/catalog-hash.ts`: canonical JSON over categories+options+actions, sha256 7. Run-level caps in runner-helper: maxActions=200, maxOutputLog=256KiB 8. Tests per handler: idempotent, path-safety negative cases, hash determinisme 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Seed bootstrap catalog (6 core + addons) prisma/seed.ts uitbreiden met seedBootstrapCatalog() die alle categorieën + opties + acties insert. Idempotent (upsert op slug). Bestanden: - `prisma/seed.ts` Stappen: 1. Open `prisma/seed.ts`; voeg seedBootstrapCatalog() toe 2. Categorieën (SINGLE/required): deploy, auth, database, ui-components, state-management, testing 3. Categorie (MULTI/optional): addons 4. Per categorie 2-4 opties met is_default-flag 5. Per optie de bijbehorende acties (COPY_FILE/CREATE_ADR_STUB/ADD_DEPENDENCY/WRITE_FILE) 6. Elke verplichte categorie genereert 1 CREATE_ADR_STUB action met number 1-6 7. Run `npm run seed`; verifieer 7 categorieën via psql 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Jobs-board BOOTSTRAP_REPO kind-uitbreidingen Alle Record<ClaudeJobKind, ...> en exhaustive switches updaten; BOOTSTRAP_REPO krijgt label/kleur/SSE-filter-set. Bestanden: - `components/jobs/job-card.tsx` - `components/jobs/jobs-column.tsx` - `lib/insights/agent-throughput.ts` - `app/api/realtime/jobs/route.ts` Stappen: 1. `components/jobs/job-card.tsx`: voeg label-mapping BOOTSTRAP_REPO → 'Bootstrap repo' 2. `components/jobs/jobs-column.tsx`: voeg kolom-titel + filter 3. `lib/insights/agent-throughput.ts`: BOOTSTRAP_REPO opnemen in kind-aggregatie (nullable cost ok) 4. `app/api/realtime/jobs/route.ts`: voeg kind toe aan initial-payload + filter 5. JobPayload-type uitbreiding: bootstrap_run_id?: string (additive extension) 6. `npm run typecheck` — alle exhaustive switches groen 2 ALIGNED_OR_PARTIAL
title description acceptance_criteria priority tasks
Sprint 1c — PAT-settings + Dry-run + Wizard config/preview User kan classic PAT plakken in settings; preview-action draait non-mutating handlers in tmpdir + Octokit-preflight; wizard heeft Configure-stap (radio/checkbox) en Preview-stap (DryRunReport). - GitHub PAT plakken in settings → "Test" toont username + scopes - PAT staat encrypted in DB (niet in plaintext) - Preview-stap toont gefilterde file-tree (cap 500), action-log, warnings - Geen DB-row in bootstrap_runs tijdens preview - Wizard accepteert geen submit zonder geslaagde preview 1
title description implementation_plan priority verify_required
lib/crypto/pat.ts AES-256-GCM encryption Encrypt-only in app-laag (decrypt leeft in bootstrap-service). Prefix 'v1:' voor toekomstige key-rotation. Bestanden: - `lib/crypto/pat.ts` (nieuw) - `lib/env.ts` (uitbreiden) - `.env.example` (instructie toevoegen) - `__tests__/lib/crypto/pat.test.ts` (nieuw) Stappen: 1. Maak `lib/crypto/pat.ts` 2. Functie encryptPat(plaintext, key) returnt 'v1:<base64-ciphertext>' 3. AES-256-GCM via Node's crypto module; random IV per call 4. Voeg BOOTSTRAP_ENCRYPTION_KEY (required, min 32) toe aan `lib/env.ts` Zod-schema 5. Voeg BOOTSTRAP_TEMPLATE_REPO (default 'madhura68/nextjs-baseline') toe 6. Tests in `__tests__/lib/crypto/pat.test.ts`: encrypt → decrypt round-trip; verschillende ciphertexts bij zelfde plaintext (IV); rejectie bij key < 32 7. Update `.env.example` met genereer-instructie (`openssl rand -base64 32`) 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
GitHubPatSettings UI + saveGitHubPatAction Settings-page sectie waar user PAT plakt. Test-knop doet Octokit-call en valideert classic-PAT scope=repo. Toon scopes + verified_at. Bestanden: - `app/(app)/settings/_components/github-pat-settings.tsx` (nieuw) - `actions/bootstrap.ts` (nieuw) - `__tests__/actions/bootstrap.test.ts` (nieuw) Stappen: 1. Maak `app/(app)/settings/_components/github-pat-settings.tsx` 2. Form met password-input (gemaskeerd) + Test-knop + Save-knop 3. UI-copy: "Vereist een classic PAT met 'repo' scope — fine-grained tokens nog niet ondersteund" 4. Server-action in `actions/bootstrap.ts` → saveGitHubPatAction(token): - Demo-check (403) - Octokit.users.getAuthenticated() → username - Parse x-oauth-scopes header → array - Reject als scope 'repo' ontbreekt - encryptPat() → store github_pat_encrypted/username/verified_at/scopes 5. UI toont na save: "✓ <username> · scopes: repo" 6. Tests in `__tests__/actions/bootstrap.test.ts`: scope-rejection; encryption-roundtrip; demo-block 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
previewBootstrapAction + dry-run executor Server-action die recipe resolved, alle non-mutating handlers in tmpdir draait, Octokit preflight doet (collision + best-effort owner-discovery), DryRunReport retourneert. Geen DB-writes. Bestanden: - `actions/bootstrap.ts` (previewBootstrapAction toevoegen) - `lib/bootstrap/recipe.ts` (nieuw) - `lib/bootstrap/dry-run.ts` (nieuw) - `__tests__/lib/bootstrap/dry-run.test.ts` (nieuw) Stappen: 1. Voeg previewBootstrapAction(productId, selections, repoOwner, repoSlug) toe aan `actions/bootstrap.ts` 2. Auth + demo-check + Zod-validate selections + GitHub-name regex 3. Resolve recipe via `lib/bootstrap/recipe.ts`: selections → BootstrapAction[] (geordend op execution_order) 4. Compute recipe_hash + catalog_version 5. Maak `lib/bootstrap/dry-run.ts`: clone template (geen cache MVP), iterate handlers met supports_dry_run=true 6. Filter file-tree: deny .git/node_modules/.next/dist/build/out/coverage/*.log/.env*/.DS_Store; cap 500 entries met truncated-flag 7. Octokit preflight: `octokit.repos.get({ owner, repo })` voor collision; `octokit.orgs.get()` voor best-effort owner-status 8. Return DryRunReport { fileTree, truncated, actionLog, warnings, canProceed, collisions } 9. Tests in `__tests__/lib/bootstrap/dry-run.test.ts`: collision-detect; path-safety enforcement; report-shape 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
BootstrapWizardDialog: Configure + Preview steps Multi-step wizard dialog vanuit product-detail-pagina. Step 1 radios/checkboxes; Step 2 toont DryRunReport; Step 3 placeholder voor status (sprint 1d). Bestanden: - `app/(app)/products/[id]/_components/bootstrap-wizard-dialog.tsx` (nieuw) - `app/(app)/products/[id]/_components/repo-owner-picker.tsx` (nieuw) - `app/(app)/products/[id]/_components/bootstrap-preview-panel.tsx` (nieuw) - `app/(app)/products/[id]/page.tsx` (Bootstrap-knop toevoegen) Stappen: 1. Maak `app/(app)/products/[id]/_components/bootstrap-wizard-dialog.tsx` 2. Volg `docs/patterns/dialog.md` Entity Dialog conventie (base-ui render-prop) 3. Step Configure: render 6 radio-groups + 1 checkbox-array (Add-ons) op basis van catalog-query 4. `repo-owner-picker.tsx`: user + orgs als opties met hint-badges (zichtbaar/onbekend/policy-blokkeert); GEEN automatisch verbergen 5. repo_slug input met GitHub-naam-regex 6. Step Preview: call previewBootstrapAction; toon `bootstrap-preview-panel.tsx` met file-tree, action-log, warnings, collision-banner 7. Voorkom Run-knop tot canProceed=true 8. `app/(app)/products/[id]/page.tsx`: Bootstrap-knop (verborgen voor demo-users) 9. Tests: wizard-state-machine; preview-roundtrip 2 ALIGNED_OR_PARTIAL
title description acceptance_criteria priority tasks
Sprint 1d — bootstrap-service + transactionele sync + E2E Sibling-repo bootstrap-service/ als nieuw Node-proces dat BOOTSTRAP_REPO-jobs claimt, recipe uitvoert via isomorphic-git (template-clone + commit + push), Octokit createRepo, met side-effect checkpoints en transactionele status-sync. Plus stale-recovery cron en realtime SSE-status panel. - bootstrap-service claimt BOOTSTRAP_REPO-jobs binnen 2s na NOTIFY - E2E: nieuw product → wizard → preview → Run → SUCCEEDED in <60s - GitHub repo bestaat met .scrum4me/bootstrap.json metadata en 6 ADR-stubs - claude_jobs.status=DONE, bootstrap_runs.status=SUCCEEDED, product.repo_url gevuld - Invalid PAT → FAILED zonder orphan repo - Twee gelijktijdige submits: één gaat door, ander krijgt unique violation - Stale-recovery cron markeert verlopen leases correct (FAILED vs FAILED_NEEDS_CLEANUP) - CI-job faalt bij hash-drift van vendor-copy 1
title description implementation_plan priority verify_required
Setup bootstrap-service sibling-repo skeleton Nieuwe directory ~/Development/bootstrap-service/ met package.json, tsconfig, Dockerfile (multi-arch arm64-primary), sync-schema.sh, sync-bootstrap-actions.sh. Bestanden (in sibling-repo `~/Development/bootstrap-service/`): - `package.json` - `tsconfig.json` - `env.ts` - `prisma/schema.prisma` (gesynced) - `sync-schema.sh` - `sync-bootstrap-actions.sh` - `Dockerfile` - `docker-compose.yml` - `README.md` Plus in Scrum4Me-repo: - `docs/manual/06-bootstrap-service.md` (nieuw) Stappen: 1. `mkdir ~/Development/bootstrap-service` 2. `package.json`: deps prisma, @prisma/client, isomorphic-git, @octokit/rest, zod 3. `tsconfig.json` met strict mode 4. `env.ts`: Zod-schema voor DATABASE_URL, DIRECT_URL, BOOTSTRAP_ENCRYPTION_KEY, BOOTSTRAP_TEMPLATE_REPO 5. `prisma/schema.prisma` symlinked of synced via `sync-schema.sh` 6. `sync-bootstrap-actions.sh` kopieert `packages/bootstrap-actions/` vanuit Scrum4Me met hash-write 7. `Dockerfile`: FROM --platform=$BUILDPLATFORM node:24-alpine, multi-arch (arm64 + amd64) 8. `docker-compose.yml`: arm64 default voor Mac dev 9. `README.md`: setup-instructies + env-template 10. Voeg `docs/manual/06-bootstrap-service.md` toe in Scrum4Me 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Claim-loop + LISTEN + lease-renewal Daemon-loop in bin/run.ts: LISTEN op scrum4me_changes filter claude_job_enqueued/BOOTSTRAP_REPO; SKIP-LOCKED claim; claimed_by_worker_id (hostname-pid-startTs); lease-renewal elke 30s. Bestanden (sibling-repo `~/Development/bootstrap-service/`): - `bin/run.ts` (nieuw) - `src/claim.ts` (nieuw) - `src/__tests__/claim.test.ts` (nieuw) Stappen: 1. `bin/run.ts`: daemon-loop 2. WORKER_ID = `${hostname}-${pid}-${startTs}` als string 3. `src/claim.ts` tryClaimBootstrapJob: UPDATE claude_jobs SET status='CLAIMED', lease_until=NOW()+60s, claimed_at, claimed_by_worker_id WHERE id=(SELECT id FROM claude_jobs WHERE status='QUEUED' AND kind='BOOTSTRAP_REPO' ORDER BY created_at FOR UPDATE SKIP LOCKED LIMIT 1) RETURNING id 4. Lease-renewal setInterval(30s) UPDATE lease_until=NOW()+60s WHERE id=? AND claimed_by_worker_id=? (only-mine guard) 5. LISTEN scrum4me_changes; bij claude_job_enqueued met kind=BOOTSTRAP_REPO → trigger claim-poll 6. Fallback poll-interval 30s 7. Tests in `src/__tests__/claim.test.ts`: SKIP-LOCKED safety bij parallel claim; lease-renewal-guard 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Execute-flow: clone + recipe + Octokit + push + checkpoints Volledige bootstrap-uitvoer: isomorphic-git clone (tag-pinned), recipe-iteratie via shared handlers, placeholder-replacement, Octokit repo-create, isomorphic-git push (PAT via onAuth-callback), .scrum4me/bootstrap.json metadata, side-effect checkpoints op DB. Bestanden (sibling-repo `~/Development/bootstrap-service/`): - `src/runner.ts` (nieuw) - `src/github.ts` (nieuw — Octokit wrapper) - `src/template-clone.ts` (nieuw — isomorphic-git) - `src/__tests__/runner.test.ts` (nieuw) Stappen: 1. `src/runner.ts`: executeRecipe(run, pat) 2. mkdtemp(); `src/template-clone.ts` isomorphic-git clone met depth=1 en ref=template_version 3. Capture template_source_sha via resolveRef HEAD 4. fs.rm tmpdir/.git; git.init met defaultBranch='main' 5. Iterate run.recipe_snapshot.actions (sorted by execution_order); ActionSchema.parse runtime 6. Dispatch per kind → handler uit `@scrum4me/bootstrap-actions` (vendor-copy) 7. replacePlaceholders(tmpdir) voor __PRODUCT_NAME__/__PRODUCT_SLUG__/__GITHUB_OWNER__ 8. writeFile `.scrum4me/bootstrap.json` met metadata (template/recipe_hash/catalog_version/etc.) 9. git.add + git.commit 10. `src/github.ts` Octokit createForAuthenticatedUser/createInOrg → checkpoint write github_repo_created_at/id/full_name 11. git.addRemote + git.push met onAuth-callback { username: 'x-access-token', password: pat } → checkpoint write push_completed_at 12. Cleanup tmpdir in finally; zeroize pat 13. Tests in `src/__tests__/runner.test.ts`: dry-run-handlers identiek aan service-handlers (geen drift); push-via-onAuth zonder URL-leak 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Transactionele status-sync (running/success/failed) syncRunning/syncSuccess/syncFailed in één prisma.$transaction met count-checks. Lease_until + claimed_by_worker_id terminal op null. NOTIFY na commit. FAILED vs FAILED_NEEDS_CLEANUP afhankelijk van github_repo_full_name. Bestanden (sibling-repo `~/Development/bootstrap-service/`): - `src/status-sync.ts` (nieuw) - `src/__tests__/status-sync.test.ts` (nieuw) Stappen: 1. `src/status-sync.ts` 2. syncRunning(runId, jobId, userId): één now=new Date(); transaction: bootstrap_runs.started_at = now WHERE status='PENDING'; claude_jobs.started_at = now WHERE status='CLAIMED'; count-check beide; rollback bij mismatch 3. syncSuccess: transaction met updateMany WHERE status='RUNNING' op zowel run als job; lease_until=null, claimed_by_worker_id=null; product.repo_url + template_version + last_bootstrap_run_id 4. syncFailed: zelfde pattern; terminal-status = run.github_repo_full_name ? 'FAILED_NEEDS_CLEANUP' : 'FAILED'; bij created repo zonder push: compensating octokit.repos.delete in catch-pad 5. NOTIFY pas na commit; status via jobStatusToApi(...) lowercase 6. Tests in `src/__tests__/status-sync.test.ts`: cancel-tijdens-success blijft CANCELLED; lease-cleanup; status-mapping 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Stale-recovery cron + service-startup recovery Verlopen BOOTSTRAP_REPO-leases (lease_until < NOW) splitsen tussen FAILED en FAILED_NEEDS_CLEANUP op basis van github_repo presence. Cron-route in app + globale startup-sweep in service. Bestanden: - `app/api/cron/bootstrap-stale-recovery/route.ts` (nieuw, in Scrum4Me) - `vercel.json` (cron-schedule toevoegen) - `~/Development/bootstrap-service/src/stale-recovery.ts` (nieuw) - `__tests__/api/cron/bootstrap-stale-recovery.test.ts` (nieuw) Stappen: 1. Maak `app/api/cron/bootstrap-stale-recovery/route.ts` met Bearer-CRON_SECRET guard 2. SQL stap 1: `UPDATE claude_jobs SET status='FAILED', error='lease expired', lease_until=null, claimed_by_worker_id=null WHERE status IN ('CLAIMED','RUNNING') AND kind='BOOTSTRAP_REPO' AND lease_until < NOW()` 3. SQL stap 2a: `UPDATE bootstrap_runs → FAILED_NEEDS_CLEANUP WHERE github_repo_full_name IS NOT NULL OR github_repo_created_at IS NOT NULL` 4. SQL stap 2b: `UPDATE bootstrap_runs → FAILED WHERE github_repo_full_name IS NULL AND github_repo_created_at IS NULL` 5. Voeg cron-schedule toe in `vercel.json` (elke 5 min) 6. `~/Development/bootstrap-service/src/stale-recovery.ts`: zelfde SQL bij service-startup (globale recovery — NIET filteren op claimed_by_worker_id) 7. Tests in `__tests__/api/cron/bootstrap-stale-recovery.test.ts`: split-strategie; kind-filter respecteert bestaande Claude-jobs ongemoeid 1 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
Service-startup logging + drift CI-verificatie Bij service-startup: log action_schema_version, schema-hash van geladen bootstrap-actions package, en catalog-version. CI faalt release bij hash-mismatch met Scrum4Me-bron. Bestanden (sibling-repo `~/Development/bootstrap-service/`): - `src/telemetry.ts` (nieuw) - `.github/workflows/release.yml` (nieuw — drift-check) Stappen: 1. `src/telemetry.ts`: bootSummary() print actionSchemaVersion, schemaHash (sha256 over geladen package src), catalogVersion (huidige DB) 2. Print bij service-startup vóór claim-loop 3. Telemetry-log gebruikt token-scrubbing helper (geen PAT/secrets in logs) 4. CI `.github/workflows/release.yml`: run `scripts/check-bootstrap-actions-hash.sh` tegen Scrum4Me-bron-hash (vergelijk via env var of release-tag) 5. Release-pipeline faalt bij drift 2 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required
BootstrapStatusPanel realtime SSE Tijdens RUNNING-fase toont wizard-step 3 realtime status via SSE op /api/realtime/jobs filtered op bootstrap_run_id. Bestanden: - `app/(app)/products/[id]/_components/bootstrap-status-panel.tsx` (nieuw) Stappen: 1. Component `app/(app)/products/[id]/_components/bootstrap-status-panel.tsx` 2. Subscribe op SSE-stream `/api/realtime/jobs` (bestaand) 3. Filter payloads op type='claude_job_status' + bootstrap_run_id=runId 4. Render status-badge (queued/running/done/failed) + progress-hints 5. Bij DONE: toon repo_url met "Open op GitHub"-link 6. Bij FAILED/FAILED_NEEDS_CLEANUP: toon error + retry-knop placeholder (fase-2) 7. Tests: SSE-event-mapping; UI-state-machine 2 ALIGNED_OR_PARTIAL
title description implementation_plan priority verify_required verify_only
E2E happy-path verificatie End-to-end test: maak product, run wizard (alle 6 core), preview, submit, wacht op SUCCEEDED, verifieer GitHub-repo en DB-state. Bestanden: - `__tests__/e2e/bootstrap-happy-path.test.ts` (nieuw) Stappen: 1. Maak product 'e2e-bootstrap-test' via UI of seed 2. Settings: PAT met repo-scope geconfigureerd voor test-user 3. Wizard: deploy=self-hosted, auth=iron-session, db=postgres-prisma, ui=shadcn-baseui, state=zustand, testing=vitest-jsdom; repo_owner=test-org 4. Preview-step → groen → Run 5. Verifieer binnen 60s: bootstrap_runs.status=SUCCEEDED; claude_jobs.status=DONE; product.repo_url niet null; product.template_version='v1.0.0' 6. Verifieer GitHub: repo bestaat private; `docs/adr/` bevat 0000+0001..0006; `.scrum4me/bootstrap.json` bevat recipe_hash/catalog_version/selected_options 7. SQL-query met JOIN: `SELECT br.status, br.repo_url, cj.lease_until > NOW() AS lease_active FROM bootstrap_runs br JOIN claude_jobs cj ON cj.id=br.claude_job_id ORDER BY br.started_at DESC NULLS LAST LIMIT 1` 8. Failure-pad: invalid PAT → FAILED + geen orphan repo 9. Demo-pad: login als demo → Bootstrap-knop verborgen; direct API → 403 1 ALIGNED_OR_PARTIAL false

M8 Bootstrap-wizard — Upload variant

Dit is de upload-variant van het volledige technische plan docs/plans/M8-bootstrap-wizard.md (v3.5). De YAML-frontmatter hierboven is bedoeld voor de "Upload plan"-functie in Scrum4Me die idea-status naar PLAN_READY brengt en daarna via materializeIdeaPlanAction een PBI met 4 Stories en bijbehorende Tasks aanmaakt.

Mapping naar het volledige plan

Story Sprint Volledige plan-sectie
Story 1 Sprint 1a — Contracten "Fasering" Sprint 1a + "Deterministic-job contract" + "Vendor-copy CI-check"
Story 2 Sprint 1b — Schema + seed + safety "Domein-model (Prisma)" + "Action-schema + path-safety" + "Seed catalog"
Story 3 Sprint 1c — PAT + Dry-run + Wizard "PAT-secret-boundary" + "Dry-run als feature" + "Wizard-componenten"
Story 4 Sprint 1d — bootstrap-service + E2E "Executor: bootstrap-service" + "Status-sync" + "Stale-recovery" + "Verificatie"

Voor uitgebreide review-historie (5 reviews), architectuur-besluiten, overwogen alternatieven, secret-boundary-onderbouwing, en open punten: zie het volledige plan-document.