diff --git a/__tests__/lib/idea-plan-parser.test.ts b/__tests__/lib/idea-plan-parser.test.ts
index 30169aa..c279ea8 100644
--- a/__tests__/lib/idea-plan-parser.test.ts
+++ b/__tests__/lib/idea-plan-parser.test.ts
@@ -62,6 +62,41 @@ body
}
})
+ it('hints when markdown sneaks into frontmatter', () => {
+ // "1. **...**: [unclosed" triggers a YAMLParseError at the markdown line
+ // (plain-list-with-bold parses as valid YAML without an unclosed flow)
+ const broken = `---
+pbi:
+ title: Test
+ priority: 2
+stories:
+1. **Toggle zichtbaar in productie**: [unclosed
+---
+
+body
+`
+ const r = parsePlanMd(broken)
+ expect(r.ok).toBe(false)
+ if (!r.ok) {
+ expect(r.errors[0].hint).toMatch(/markdown/i)
+ expect(r.errors[0].line).toBeGreaterThan(1)
+ }
+ })
+
+ it('omits hint for non-markdown yaml errors', () => {
+ const broken = `---
+pbi:
+ title: Test
+ priority: [unclosed
+stories:
+ - foo
+---
+`
+ const r = parsePlanMd(broken)
+ expect(r.ok).toBe(false)
+ if (!r.ok) expect(r.errors[0].hint).toBeUndefined()
+ })
+
it('reports schema-validation error when pbi-section missing', () => {
const noPbi = `---
stories:
diff --git a/components/ideas/idea-md-editor.tsx b/components/ideas/idea-md-editor.tsx
index 1d7c46e..85c52bd 100644
--- a/components/ideas/idea-md-editor.tsx
+++ b/components/ideas/idea-md-editor.tsx
@@ -123,6 +123,9 @@ export function IdeaMdEditor({ ideaId, kind, initialValue, onCancel }: Props) {
{err.line ? `Regel ${err.line}: ` : ''}
{err.message}
+ {err.hint && (
+ Tip: {err.hint}
+ )}
))}
diff --git a/lib/idea-plan-parser.ts b/lib/idea-plan-parser.ts
index 02b968b..b8202b9 100644
--- a/lib/idea-plan-parser.ts
+++ b/lib/idea-plan-parser.ts
@@ -13,7 +13,7 @@ import {
type IdeaPlanFrontmatter,
} from '@/lib/schemas/idea'
-export type PlanParseError = { line?: number; message: string }
+export type PlanParseError = { line?: number; message: string; hint?: string }
export type PlanParseResult =
| { ok: true; plan: IdeaPlanFrontmatter; body: string }
@@ -43,14 +43,23 @@ export function parsePlanMd(md: string): PlanParseResult {
parsed = parseYaml(frontmatterRaw)
} catch (err) {
if (err instanceof YAMLParseError) {
+ const yamlLine = err.linePos?.[0]?.line
+ const fileLine = yamlLine != null ? yamlLine + 1 : undefined
+ const offendingLine =
+ yamlLine != null
+ ? frontmatterRaw.split(/\r?\n/)[(yamlLine ?? 1) - 1]
+ : undefined
+ const isMarkdown =
+ offendingLine != null &&
+ (/^\s*\d+\.\s+\*\*/.test(offendingLine) ||
+ /^\s*[-*]\s+\*\*/.test(offendingLine) ||
+ /^\s*\d+\..*:/.test(offendingLine))
+ const hint = isMarkdown
+ ? 'Lijkt op markdown-content (genummerde of opsommingslijst) binnen YAML-frontmatter. Verplaats deze regels naar na de afsluitende `---`, of zet ze in een `description: |` blok.'
+ : undefined
return {
ok: false,
- errors: [
- {
- line: err.linePos?.[0]?.line,
- message: err.message,
- },
- ],
+ errors: [{ line: fileLine, message: err.message, hint }],
}
}
return {