{"record":{"id":"18fd289341351a8a","repo":"Fission-AI/OpenSpec","slug":"change-must-have-a-why-section","errorCode":null,"errorMessage":"Change must have a Why section","messagePattern":"Change must have a Why section","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/core/parsers/change-parser.ts","lineNumber":28,"sourceCode":"  requirements: Requirement[];\n  renames?: Array<{ from: string; to: string }>;\n}\n\nexport class ChangeParser extends MarkdownParser {\n  private changeDir: string;\n\n  constructor(content: string, changeDir: string) {\n    super(content);\n    this.changeDir = changeDir;\n  }\n\n  async parseChangeWithDeltas(name: string): Promise<Change> {\n    const sections = this.parseSections();\n    const why = this.findSection(sections, 'Why')?.content || '';\n    const whatChanges = this.findSection(sections, 'What Changes')?.content || '';\n    \n    if (!why) {\n      throw new Error('Change must have a Why section');\n    }\n    \n    if (!whatChanges) {\n      throw new Error('Change must have a What Changes section');\n    }\n\n    // Parse deltas from the What Changes section (simple format)\n    const simpleDeltas = this.parseDeltas(whatChanges);\n    \n    // Check if there are spec files with delta format\n    const specsDir = path.join(this.changeDir, 'specs');\n    const deltaDeltas = await this.parseDeltaSpecs(specsDir);\n    \n    // Combine both types of deltas, preferring delta format if available\n    const deltas = deltaDeltas.length > 0 ? deltaDeltas : simpleDeltas;\n\n    return {\n      name,","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/Fission-AI/OpenSpec/blob/6926ccb18afa4ff621112813e9968334576ee11a/src/core/parsers/change-parser.ts#L10-L46","documentation":"Thrown by parseChangeWithDeltas when the change document's 'Why' section is missing or empty. Structural validation of proposal.md: every change must explain its motivation, so parsing fails early rather than archiving an unexplained change; input at fault is the change markdown lacking a populated Why heading.","triggerScenarios":"Thrown at src/core/parsers/change-parser.ts:28 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Add a '## Why' section to the change markdown explaining the motivation","Check the heading spelling and heading level (##)","Ensure the section body is non-empty"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"6926ccb18afa4ff621112813e9968334576ee11a","analyzedAt":"2026-08-25T12:29:45.729Z","contentChangedAt":"2026-08-25T12:29:45.729Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}