From b8e22539f6211ffd551b0f68e117e12157e734ab Mon Sep 17 00:00:00 2001 From: Janpeter Visser <30029041+madhura68@users.noreply.github.com> Date: Thu, 14 May 2026 00:26:39 +0000 Subject: [PATCH] fix(ideas): plan-upload task-volgorde + verifier-pad-format docs (#198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(ideas): respecteer YAML-volgorde bij plan-materialize Tasks erven nu story-priority i.p.v. eigen task.priority bij materializeIdeaPlanAction. Worker sorteert op `priority ASC, sort_order ASC`; gemixte task-priorities binnen één story zouden anders de YAML-volgorde verstoren (e.g. tasks met priority 1/1/1/2/1/2 → worker-volgorde 1,2,3,5,4,6 i.p.v. 1,2,3,4,5,6). - actions/ideas.ts: priority = s.priority bij task-create - lib/schemas/idea.ts: task.priority optional (geaccepteerd, genegeerd) - lib/idea-prompts/make-plan.md: documenteer dat task.priority genegeerd wordt - __tests__/lib/idea-schemas.test.ts: test dat omitted task.priority slaagt Co-Authored-By: Claude Opus 4.7 (1M context) * docs(make-plan): documenteer backtick-format voor implementation_plan-paden verify_task_against_plan extraheert paden uit implementation_plan via twee regex-patronen (backticks + bullet). Paden inline in genummerde tekst-stappen worden niet herkend → planPaths.length=0 → bij diff >50 regels DIVERGENT. Voeg sectie "Bestandspaden in implementation_plan — verplicht format" toe die uitlegt welke formats werken (backticks, bullets) en welke niet (inline). Plus verband met verify_required-keuze: default ALIGNED_OR_PARTIAL behouden voor ADR-stubs en multi-file edits. Voorkomt herhaling van T-963 cancelled_by_self-symptoom waar implementatie slaagde maar verifier DIVERGENT teruggaf. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- __tests__/lib/idea-schemas.test.ts | 17 +++++++++ actions/ideas.ts | 6 ++- lib/idea-prompts/make-plan.md | 61 +++++++++++++++++++++++++++++- lib/schemas/idea.ts | 7 +++- 4 files changed, 87 insertions(+), 4 deletions(-) diff --git a/__tests__/lib/idea-schemas.test.ts b/__tests__/lib/idea-schemas.test.ts index 1514f5d..637ce1c 100644 --- a/__tests__/lib/idea-schemas.test.ts +++ b/__tests__/lib/idea-schemas.test.ts @@ -128,4 +128,21 @@ describe('ideaPlanMdFrontmatterSchema', () => { }) expect(r.success).toBe(false) }) + + it('accepts plan with task.priority omitted (inherits story-priority via materialize)', () => { + const r = ideaPlanMdFrontmatterSchema.safeParse({ + ...validPlan, + stories: [ + { + title: 'Story zonder task-priorities', + priority: 2, + tasks: [ + { title: 'Taak 1' }, // geen priority — moet geaccepteerd + { title: 'Taak 2', verify_required: 'ALIGNED' }, + ], + }, + ], + }) + expect(r.success).toBe(true) + }) }) diff --git a/actions/ideas.ts b/actions/ideas.ts index 94dfc4d..0a2ad09 100644 --- a/actions/ideas.ts +++ b/actions/ideas.ts @@ -738,7 +738,11 @@ export async function materializeIdeaPlanAction( title: t.title, description: t.description ?? null, implementation_plan: t.implementation_plan ?? null, - priority: t.priority, + // Erf priority van de story zodat YAML-volgorde gerespecteerd + // blijft. Worker sorteert op `priority ASC, sort_order ASC`; + // gemixte task-priorities binnen één story zouden anders de + // YAML-volgorde verstoren (zie plan-fix task-volgorde-na-upload). + priority: s.priority, sort_order: ti + 1, status: 'TO_DO', verify_required: t.verify_required ?? 'ALIGNED_OR_PARTIAL', diff --git a/lib/idea-prompts/make-plan.md b/lib/idea-prompts/make-plan.md index 86891a0..d4852cb 100644 --- a/lib/idea-prompts/make-plan.md +++ b/lib/idea-prompts/make-plan.md @@ -101,7 +101,8 @@ stories: 1. Bestand X aanpassen — concrete steps 2. Test toevoegen Y 3. Verifieer Z - priority: 2 + # task.priority is optioneel en wordt door materialize GENEGEERD. + # Tasks erven story.priority; sort_order = positie in deze tasks-array. verify_required: ALIGNED_OR_PARTIAL # ALIGNED | ALIGNED_OR_PARTIAL | ANY verify_only: false # true voor pure verify-passes - title: "Taak B" @@ -142,7 +143,12 @@ Beschrijf: ## Validatie-regels die de parser afdwingt - `pbi.title`: 1–200 chars, **verplicht**. -- `pbi.priority`, `story.priority`, `task.priority`: integer 1–4. +- `pbi.priority`, `story.priority`: integer 1–4, **verplicht**. +- `task.priority`: integer 1–4, **optioneel**. **Wordt door materialize genegeerd** + ten faveure van story-priority — alle tasks binnen een story erven dezelfde + priority. Reden: worker sorteert op `priority ASC, sort_order ASC`; gemixte + task-priorities zouden de YAML-volgorde verstoren. De YAML-volgorde *is* de + execution-volgorde — daar is `sort_order` (positie in de array) voor. - Minimaal 1 story; per story minimaal 1 taak. - `implementation_plan`: max 8000 chars. - `verify_required`: enum exact `ALIGNED` | `ALIGNED_OR_PARTIAL` | `ANY`. @@ -164,3 +170,54 @@ regelnummers; de gebruiker kan re-plan klikken of `plan_md` handmatig fixen. ❌ **Slecht**: "Maak de feature werkend" ✅ **Goed**: "Voeg `actions/ideas.ts:createIdeaAction(input)` toe — auth + demo-403 + zod-parse + nextIdeaCode + prisma.idea.create + revalidatePath" + +## Bestandspaden in `implementation_plan` — verplicht format + +`verify_task_against_plan` extraheert bestandspaden uit `implementation_plan` +om de git-diff tegen het plan te valideren. Zonder herkenbare paden valt de +verifier terug op een line-count-heuristiek (`> 50 regels → DIVERGENT`), +waardoor zelfs correct uitgevoerde tasks `cancelled_by_self` kunnen worden +(verify-gate weigert `DONE` te zetten). + +De path-extractor herkent twee formats: + +1. **Backticks** (aanbevolen voor inline-paden binnen een zin of stap): + `lib/foo.ts`, `app/(app)/products/[id]/page.tsx`, `docs/adr/0009-foo.md` + +2. **Bullet-lijst** (aanbevolen voor doel-bestanden vóór de stappen): + - `lib/foo.ts` + - `app/(app)/products/[id]/page.tsx` + +❌ **Werkt NIET** (paden inline in genummerde tekst zonder markup): + +``` +1. Maak docs/adr/0009-foo.md aan op basis van templates/nygard.md +2. Update docs/INDEX.md via npm run docs +``` + +→ Path-extractor herkent geen pad → `planPaths.length=0` → bij diff >50 regels +→ verifier returnt **DIVERGENT** → task met `verify_required=ALIGNED` faalt. + +✅ **Werkt WEL** (zelfde stappen, paden in backticks): + +``` +1. Maak `docs/adr/0009-foo.md` aan op basis van `docs/adr/templates/nygard.md` +2. Update `docs/INDEX.md` via `npm run docs` +``` + +→ Path-extractor vindt 3 paden → diff bevat dezelfde 3 paden → coverage 100% +→ verifier returnt **ALIGNED** → task gaat naar DONE. + +**Regel**: noem **elk** bestand dat de task aanmaakt, bewerkt of regenereert +in backticks of als bullet. Vermeld ook `docs/INDEX.md` als die regenereerd +wordt door `npm run docs`, en `README.md`-achtige updates. + +**Verband met `verify_required`**: +- `ALIGNED`: alle plan-paden moeten in de diff zitten + ratio diff/plan <3 +- `ALIGNED_OR_PARTIAL`: PARTIAL toegestaan mits worker summary ≥20 chars geeft +- `ANY`: geen verifier-gate + +Default = `ALIGNED_OR_PARTIAL` (schema). Kies `ALIGNED` alleen wanneer +plan-paden 1-op-1 matchen met te-wijzigen-bestanden en de diff klein is +(<50 regels typisch). Voor ADR-stubs, schema-migraties of multi-file refactors: +laat `ALIGNED_OR_PARTIAL` staan. diff --git a/lib/schemas/idea.ts b/lib/schemas/idea.ts index 4be1553..a8a32f2 100644 --- a/lib/schemas/idea.ts +++ b/lib/schemas/idea.ts @@ -20,11 +20,16 @@ export type IdeaUpdateInput = z.infer const verifyRequiredEnum = z.enum(['ALIGNED', 'ALIGNED_OR_PARTIAL', 'ANY']) +// Task-level priority is geaccepteerd in het schema voor backward-compat, +// maar wordt door `materializeIdeaPlanAction` genegeerd ten faveure van +// story-priority. Reden: worker sorteert op `priority ASC, sort_order ASC` +// — gemixte task-priorities binnen één story zouden de YAML-volgorde +// verstoren. Auteurs hoeven het veld dus niet meer in te vullen. const planTaskSchema = z.object({ title: z.string().min(1).max(200), description: z.string().max(4000).optional(), implementation_plan: z.string().max(8000).optional(), - priority: z.number().int().min(1).max(4), + priority: z.number().int().min(1).max(4).optional(), verify_required: verifyRequiredEnum.optional(), verify_only: z.boolean().optional(), })