{"record":{"id":"a552bb627cf8e954","repo":"vectordotdev/vector","slug":"exactly-one-summary-section-is-required","errorCode":null,"errorMessage":"exactly one `## Summary` section is required","messagePattern":"exactly one `## Summary` section is required","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vdev/src/commands/changelog/mod.rs","lineNumber":134,"sourceCode":"    if !normalized.ends_with('\\n') {\n        normalized.push('\\n');\n    }\n    let body: &str = &normalized;\n\n    // 1) File must begin with an H1 title on the first line — no leading blank lines.\n    let title_body = body\n        .strip_prefix(\"# \")\n        .ok_or_else(|| anyhow::anyhow!(\"first line must be an H1 title (`# ...`)\"))?;\n    let (title_line, after_title) = title_body.split_once('\\n').unwrap_or((title_body, \"\"));\n    let (title_text, anchor) = split_title_and_anchor(title_line);\n    let title = title_text.to_string();\n    if title.is_empty() {\n        bail!(\"H1 title must not be empty\");\n    }\n\n    // 2) Exactly one `## Summary` and one `## Migration`, in that order.\n    if after_title.matches(SUMMARY_MARKER).count() != 1 {\n        bail!(\"exactly one `## Summary` section is required\");\n    }\n    if after_title.matches(MIGRATION_MARKER).count() != 1 {\n        bail!(\"exactly one `## Migration` section is required\");\n    }\n    let s_pos = after_title.find(SUMMARY_MARKER).unwrap();\n    let m_pos = after_title.find(MIGRATION_MARKER).unwrap();\n    if s_pos >= m_pos {\n        bail!(\"`## Summary` must come before `## Migration`\");\n    }\n\n    // Reject any prose between the H1 title and `## Summary`. Someone hand-migrating an\n    // old free-form breaking fragment could carry over the previous body here and it\n    // would silently disappear from both the release CUE and the upgrade guide.\n    // Everything before Summary must be whitespace (`\\n## Summary\\n` starts with the\n    // leading newline, so `s_pos` includes it).\n    if let Some(prefix) = after_title.get(..s_pos)\n        && !prefix.trim().is_empty()\n    {","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/vdev/src/commands/changelog/mod.rs#L116-L152","documentation":"`parse_breaking_sections` requires a breaking fragment to contain exactly one `## Summary` section. If the text after the H1 title contains zero or multiple occurrences of the SUMMARY_MARKER, it bails with this error. The Summary section feeds the release CUE, so its presence and uniqueness are enforced structurally.","triggerScenarios":"Parsing a breaking fragment (via vdev changelog validation) that omits `## Summary` entirely, spells it differently (e.g. `## summary`, `# Summary`), or duplicates the `## Summary` heading.","commonSituations":"Creating a fragment from scratch without using `vdev changelog new`'s template; copy-pasting sections and duplicating the Summary heading; hand-migrating old free-form fragments that never had Summary sections.","solutions":["Add exactly one `## Summary` section after the H1 title.","Remove duplicate `## Summary` headings, merging their content.","Regenerate the fragment with `vdev changelog new <slug> --breaking` (or equivalent) to start from the correct template."],"exampleFix":"// before\n# My change\nSome prose...\n// after\n# My change\n## Summary\nDescribe the breaking change here.\n## Migration\nHow to migrate.","handlingStrategy":"validation","validationCode":"let summary_count = text.matches(\"## Summary\").count();\nif summary_count != 1 {\n    eprintln!(\"fragment must contain exactly one '## Summary' (found {summary_count})\");\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = parse_breaking_sections(text) {\n    eprintln!(\"fix fragment structure: {e}\");\n}","preventionTips":["Use the `vdev changelog new` template so Summary/Migration headings exist verbatim.","Match the heading exactly: `## Summary`, correct casing and level.","Avoid copy-pasting fragments in ways that duplicate headings."],"tags":["changelog","validation","markdown"],"backgroundTag":"missing-required-config-field","analyzedSha":"bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013","analyzedAt":"2026-09-16T02:53:35.741Z","contentChangedAt":"2026-09-16T02:53:35.741Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}