From 71be6f1092899f74de713f0bc568803c270d1608 Mon Sep 17 00:00:00 2001 From: Jeffery Utter Date: Sat, 31 Jan 2026 18:10:17 -0600 Subject: [PATCH] fix: prevent markdown horizontal rules from being parsed as frontmatter The query command was incorrectly treating markdown content after horizontal rules (---) as YAML frontmatter fields, producing invalid JSON with extra fields from the markdown body. The awk script toggled in_front on every --- line, but should only process the first two (opening and closing of frontmatter block). Track frontmatter completion with frontmatter_done flag to ignore subsequent --- markers in markdown content. Add test case with horizontal rules and YAML-like content to prevent regression. --- features/steps/ticket_steps.py | 41 ++++++++++++++++++++++++++++++++++ features/ticket_query.feature | 10 +++++++++ ticket | 10 +++++++-- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/features/steps/ticket_steps.py b/features/steps/ticket_steps.py index 276eb278..f8537bda 100644 --- a/features/steps/ticket_steps.py +++ b/features/steps/ticket_steps.py @@ -176,6 +176,30 @@ def step_ticket_has_notes(context, ticket_id): ticket_path.write_text(content) +@given(r'ticket "(?P[^"]+)" has markdown content with horizontal rule') +def step_ticket_has_horizontal_rule(context, ticket_id): + """Add markdown content with a horizontal rule (---) to ticket.""" + ticket_path = Path(context.test_dir) / '.tickets' / f'{ticket_id}.md' + content = ticket_path.read_text() + + # Add markdown content with horizontal rule separator and YAML-like content after it + # This simulates the issue where content after --- looks like YAML frontmatter + additional_content = ''' +## Section 1 + +Some content here. + +--- + +## Implementation Plan + +Overview: This is a detailed plan +Design Decision: We chose this approach +''' + content += additional_content + ticket_path.write_text(content) + + @given(r'I am in subdirectory "(?P[^"]+)"') def step_in_subdirectory(context, subdir): """Change to a subdirectory (creating it if needed).""" @@ -591,6 +615,23 @@ def step_jsonl_deps_is_array(context): raise AssertionError("No JSONL line with deps field found") +@then(r'the JSONL should have exactly (?P\d+) fields') +def step_jsonl_exact_field_count(context, count): + """Assert JSONL has exactly N fields.""" + count = int(count) + lines = context.stdout.strip().split('\n') + assert lines, "No JSONL output" + + for line in lines: + if line.strip(): + data = json.loads(line) + actual_count = len(data.keys()) + assert actual_count == count, \ + f"Expected {count} fields but got {actual_count}\nFields: {list(data.keys())}\nFull line: {line}" + return + raise AssertionError("No JSONL output found") + + @then(r'the dep tree output should have (?P[^\s]+) before (?P[^\s]+)') def step_dep_tree_order(context, first_id, second_id): """Assert that first_id appears before second_id in dep tree output.""" diff --git a/features/ticket_query.feature b/features/ticket_query.feature index 2ccafe70..b7e10f80 100644 --- a/features/ticket_query.feature +++ b/features/ticket_query.feature @@ -46,3 +46,13 @@ Feature: Ticket Query When I run "ticket query" Then the command should succeed And the JSONL deps field should be a JSON array + + Scenario: Query handles markdown horizontal rules + Given a ticket exists with ID "query-001" and title "Ticket with horizontal rule" + And ticket "query-001" has markdown content with horizontal rule + When I run "ticket query" + Then the command should succeed + And the output should be valid JSONL + And the JSONL output should have field "id" + And the JSONL output should have field "status" + And the JSONL should have exactly 7 fields diff --git a/ticket b/ticket index 452adda4..dc0ca1c9 100755 --- a/ticket +++ b/ticket @@ -1325,10 +1325,16 @@ cmd_query() { BEGIN { FS=": "; in_front=0 } FNR==1 { if (prev_file) emit() - field_count=0; in_front=0 + field_count=0; in_front=0; frontmatter_done=0 prev_file=FILENAME } - /^---$/ { in_front = !in_front; next } + /^---$/ { + if (!frontmatter_done) { + in_front = !in_front + if (!in_front) frontmatter_done = 1 + } + next + } in_front && /^[a-zA-Z]/ { key = $1 val = substr($0, length($1) + 3)