fix(ideas): plan-upload task-volgorde + verifier-pad-format docs (#198)
* 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) <noreply@anthropic.com> * 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) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
76c2efd27c
commit
b8e22539f6
4 changed files with 87 additions and 4 deletions
|
|
@ -128,4 +128,21 @@ describe('ideaPlanMdFrontmatterSchema', () => {
|
||||||
})
|
})
|
||||||
expect(r.success).toBe(false)
|
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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -738,7 +738,11 @@ export async function materializeIdeaPlanAction(
|
||||||
title: t.title,
|
title: t.title,
|
||||||
description: t.description ?? null,
|
description: t.description ?? null,
|
||||||
implementation_plan: t.implementation_plan ?? 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,
|
sort_order: ti + 1,
|
||||||
status: 'TO_DO',
|
status: 'TO_DO',
|
||||||
verify_required: t.verify_required ?? 'ALIGNED_OR_PARTIAL',
|
verify_required: t.verify_required ?? 'ALIGNED_OR_PARTIAL',
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,8 @@ stories:
|
||||||
1. Bestand X aanpassen — concrete steps
|
1. Bestand X aanpassen — concrete steps
|
||||||
2. Test toevoegen Y
|
2. Test toevoegen Y
|
||||||
3. Verifieer Z
|
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_required: ALIGNED_OR_PARTIAL # ALIGNED | ALIGNED_OR_PARTIAL | ANY
|
||||||
verify_only: false # true voor pure verify-passes
|
verify_only: false # true voor pure verify-passes
|
||||||
- title: "Taak B"
|
- title: "Taak B"
|
||||||
|
|
@ -142,7 +143,12 @@ Beschrijf:
|
||||||
## Validatie-regels die de parser afdwingt
|
## Validatie-regels die de parser afdwingt
|
||||||
|
|
||||||
- `pbi.title`: 1–200 chars, **verplicht**.
|
- `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.
|
- Minimaal 1 story; per story minimaal 1 taak.
|
||||||
- `implementation_plan`: max 8000 chars.
|
- `implementation_plan`: max 8000 chars.
|
||||||
- `verify_required`: enum exact `ALIGNED` | `ALIGNED_OR_PARTIAL` | `ANY`.
|
- `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"
|
❌ **Slecht**: "Maak de feature werkend"
|
||||||
✅ **Goed**: "Voeg `actions/ideas.ts:createIdeaAction(input)` toe — auth +
|
✅ **Goed**: "Voeg `actions/ideas.ts:createIdeaAction(input)` toe — auth +
|
||||||
demo-403 + zod-parse + nextIdeaCode + prisma.idea.create + revalidatePath"
|
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.
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,16 @@ export type IdeaUpdateInput = z.infer<typeof ideaUpdateSchema>
|
||||||
|
|
||||||
const verifyRequiredEnum = z.enum(['ALIGNED', 'ALIGNED_OR_PARTIAL', 'ANY'])
|
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({
|
const planTaskSchema = z.object({
|
||||||
title: z.string().min(1).max(200),
|
title: z.string().min(1).max(200),
|
||||||
description: z.string().max(4000).optional(),
|
description: z.string().max(4000).optional(),
|
||||||
implementation_plan: z.string().max(8000).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_required: verifyRequiredEnum.optional(),
|
||||||
verify_only: z.boolean().optional(),
|
verify_only: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue