diff --git a/PRODUCT_SPEC.md b/PRODUCT_SPEC.md index 1d9453f..9e18776 100644 --- a/PRODUCT_SPEC.md +++ b/PRODUCT_SPEC.md @@ -159,7 +159,13 @@ The product shall provide a diagnostic command that validates publishing prerequ ## Title Rule -The first H1 heading becomes the issue title. +The first heading at the top of the markdown becomes the issue title. + +Supported heading levels: + +* H1 +* H2 +* H3 Example: @@ -173,6 +179,8 @@ becomes Add timestamps to logging system ``` +The same rule applies to the first `##` or `###` heading when it appears at the top of the markdown. + --- ## Body Rule diff --git a/TASKS.md b/TASKS.md index 9623ab4..a784286 100644 --- a/TASKS.md +++ b/TASKS.md @@ -21,7 +21,7 @@ Acceptance Criteria: -* Markdown with a first H1 heading produces a valid issue draft +* Markdown with a first H1, H2, or H3 heading produces a valid issue draft * Markdown without content is rejected * Missing title is rejected diff --git a/internal/compliance/compliance_test.go b/internal/compliance/compliance_test.go index da9499c..fa437fb 100644 --- a/internal/compliance/compliance_test.go +++ b/internal/compliance/compliance_test.go @@ -23,7 +23,7 @@ func TestFrameworkCompliance(t *testing.T) { func TestArchitectureCompliance(t *testing.T) { root := repoRoot(t) - assertContains(t, root, "PRODUCT_SPEC.md", "The first H1 heading becomes the issue title.") + assertContains(t, root, "PRODUCT_SPEC.md", "The first heading at the top of the markdown becomes the issue title.") assertContains(t, root, "ARCHITECTURE.md", "Author != Publisher") assertContains(t, root, "FRAMEWORK.md", "AST-based parsing") assertContains(t, root, "FRAMEWORK.md", "Single Binary CLI") diff --git a/internal/extraction/extractor.go b/internal/extraction/extractor.go index 0eed0ad..2204eef 100644 --- a/internal/extraction/extractor.go +++ b/internal/extraction/extractor.go @@ -37,7 +37,7 @@ func extractTitleAndBody(md string) (string, string) { return ast.WalkContinue, nil } heading, ok := node.(*ast.Heading) - if !ok || heading.Level != 1 { + if !ok || heading.Level < 1 || heading.Level > 3 { return ast.WalkContinue, nil } diff --git a/internal/extraction/extractor_test.go b/internal/extraction/extractor_test.go index c1a8ee2..e0d4b25 100644 --- a/internal/extraction/extractor_test.go +++ b/internal/extraction/extractor_test.go @@ -24,6 +24,20 @@ func TestExtractIssue(t *testing.T) { wantBody: "Current logs do not contain timestamps.", wantErr: false, }, + { + name: "with h2 title", + content: "## Add timestamps to logging\n\nCurrent logs do not contain timestamps.", + wantTitle: "Add timestamps to logging", + wantBody: "Current logs do not contain timestamps.", + wantErr: false, + }, + { + name: "with h3 title", + content: "### Add timestamps to logging\n\nCurrent logs do not contain timestamps.", + wantTitle: "Add timestamps to logging", + wantBody: "Current logs do not contain timestamps.", + wantErr: false, + }, { name: "with inline code title", content: "# Add `--version` Support\n\nBody text.",