{"record":{"id":"05f3ea3fb51b9030","repo":"vectordotdev/vector","slug":"invalid-breaking-fragment-filename-title-still-contains-the","errorCode":null,"errorMessage":"invalid breaking fragment '{filename}': title still contains the scaffolder placeholder 'TODO'.","messagePattern":"invalid breaking fragment '(.+?)': title still contains the scaffolder placeholder 'TODO'\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vdev/src/commands/check/changelog_fragments.rs","lineNumber":270,"sourceCode":"/// <markdown — \"Action needed\" body for the upgrade guide, or \"N/A\">\n///\n/// authors: <name> [<name> ...]\n/// ```\nfn validate_breaking_fragment(content: &str, filename: &str) -> Result<()> {\n    // Strip the trailing `authors:` line before parsing — it's not part of the markdown\n    // structure and would otherwise land inside the Migration section.\n    let body_before_authors = content\n        .rsplit_once(\"\\nauthors: \")\n        .map_or(content, |(before, _)| before);\n\n    let sections = parse_breaking_sections(body_before_authors)\n        .map_err(|e| anyhow::anyhow!(\"invalid breaking fragment '{filename}': {e}\"))?;\n\n    if sections.title.is_empty() {\n        bail!(\"invalid breaking fragment '{filename}': title must not be empty.\");\n    }\n    if sections.title.contains(\"TODO\") {\n        bail!(\n            \"invalid breaking fragment '{filename}': title still contains the scaffolder placeholder 'TODO'.\"\n        );\n    }\n    if let Some(anchor) = sections.anchor.as_deref()\n        && !is_valid_anchor(anchor)\n    {\n        bail!(\n            \"invalid breaking fragment '{filename}': anchor must be a lowercase kebab-case slug (a-z, 0-9, hyphens); got '{anchor}'.\"\n        );\n    }\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use indoc::{formatdoc, indoc};","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/vdev/src/commands/check/changelog_fragments.rs#L252-L288","documentation":"`vdev check changelog-fragments` rejects a breaking fragment whose title still contains the literal placeholder 'TODO' left by the fragment scaffolder. This catches fragments created from the template but never actually filled in.","triggerScenarios":"A breaking fragment file was generated from the scaffold template and committed with the title still containing 'TODO', e.g. `TODO: describe the change`.","commonSituations":"Contributor scaffolds a fragment early, forgets to replace the TODO before opening a PR; CI then fails the changelog-fragments check.","solutions":["Replace the TODO placeholder in the title with a real description of the breaking change.","Re-run `make check-changelog-fragments` locally before pushing."],"exampleFix":"// before\n### TODO\n// after\n### \"`http` source\": removed deprecated `headers` config option","handlingStrategy":"validation","validationCode":"let title = /* parsed title */;\nif title.contains(\"TODO\") { panic!(\"fill in the TODO before committing the fragment\"); }","typeGuard":"fn title_filled(title: &str) -> bool { !title.is_empty() && !title.contains(\"TODO\") }","tryCatchPattern":null,"preventionTips":["Treat scaffold TODOs as uncommitted work; never `git add -A` them.","Review fragment diffs for 'TODO' before opening the PR.","Use a grep pre-commit hook: `! grep -q TODO <fragment>`."],"tags":["changelog","validation","placeholder","ci"],"backgroundTag":"schema-validation-failed","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"}