feat: Fix Issue #46 - Schema generation outline mode draft integration
Resolve the integration issue where outline mode schema generation captured heading text correctly but draft generation didn't use it, resulting in generic placeholders instead of preserved document structure. Key changes: - Enhanced StubGenerator._extract_heading_text_from_schema() to extract actual heading text from enum constraints - Modified heading generation logic in _generate_content_from_headings() to use captured text - Fixed both H1 and H2+ heading handling to preserve source document structure - Added comprehensive test suite covering all outline mode functionality - Updated end-to-end test to reflect expected behavior (stubs vs full validation) Impact: - Outline schemas now properly integrate with draft generation - Generated drafts preserve actual heading text from source documents - End-to-end workflow: example → outline schema → draft maintains document structure - Backward compatibility maintained for existing functionality Tests: 8/8 passing in test_issue_46_schema_generation_outline.py Resolves: coulomb/markitect_project#46 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -138,11 +138,15 @@ class StubGenerator:
|
||||
|
||||
# Generate the content with proper hierarchy
|
||||
if 1 in heading_counts:
|
||||
# Start with H1
|
||||
lines.append(f"# {doc_title}")
|
||||
lines.append("")
|
||||
# Get the heading schema for level 1
|
||||
level_1_heading_schema = heading_properties.get('level_1', {})
|
||||
|
||||
# Try to extract actual H1 heading text from schema, fallback to doc_title
|
||||
h1_text = self._extract_heading_text_from_schema(level_1_heading_schema, 0) or doc_title
|
||||
|
||||
# Start with H1
|
||||
lines.append(f"# {h1_text}")
|
||||
lines.append("")
|
||||
lines.append(self._get_placeholder_content(
|
||||
placeholder_style,
|
||||
"introduction",
|
||||
@@ -159,15 +163,18 @@ class StubGenerator:
|
||||
count = heading_counts[level]
|
||||
for i in range(count):
|
||||
heading_prefix = '#' * level
|
||||
section_name = self._generate_section_name(level, i + 1)
|
||||
|
||||
lines.append(f"{heading_prefix} {section_name}")
|
||||
lines.append("")
|
||||
|
||||
# Get the heading schema for this level
|
||||
level_key = f"level_{level}"
|
||||
heading_schema = heading_properties.get(level_key, {})
|
||||
|
||||
# Try to extract actual heading text from schema enum constraints
|
||||
section_name = self._extract_heading_text_from_schema(heading_schema, i) or \
|
||||
self._generate_section_name(level, i + 1)
|
||||
|
||||
lines.append(f"{heading_prefix} {section_name}")
|
||||
lines.append("")
|
||||
|
||||
lines.append(self._get_placeholder_content(
|
||||
placeholder_style,
|
||||
f"section_level_{level}",
|
||||
@@ -181,18 +188,23 @@ class StubGenerator:
|
||||
count = heading_counts[level]
|
||||
for i in range(count):
|
||||
heading_prefix = '#' * level
|
||||
if level == min(heading_counts.keys()) and i == 0:
|
||||
section_name = doc_title
|
||||
else:
|
||||
section_name = self._generate_section_name(level, i + 1)
|
||||
|
||||
lines.append(f"{heading_prefix} {section_name}")
|
||||
lines.append("")
|
||||
|
||||
# Get the heading schema for this level
|
||||
level_key = f"level_{level}"
|
||||
heading_schema = heading_properties.get(level_key, {})
|
||||
|
||||
# Try to extract actual heading text from schema enum constraints
|
||||
if level == min(heading_counts.keys()) and i == 0:
|
||||
# For the first heading of the minimum level, try schema first, then doc_title
|
||||
section_name = self._extract_heading_text_from_schema(heading_schema, i) or doc_title
|
||||
else:
|
||||
# For other headings, try schema first, then fallback to generic names
|
||||
section_name = self._extract_heading_text_from_schema(heading_schema, i) or \
|
||||
self._generate_section_name(level, i + 1)
|
||||
|
||||
lines.append(f"{heading_prefix} {section_name}")
|
||||
lines.append("")
|
||||
|
||||
lines.append(self._get_placeholder_content(
|
||||
placeholder_style,
|
||||
f"section_level_{level}",
|
||||
@@ -318,4 +330,29 @@ TODO: Add detailed content for this subsection.""",
|
||||
if isinstance(instruction_schema, dict):
|
||||
return instruction_schema.get('const')
|
||||
|
||||
return None
|
||||
|
||||
def _extract_heading_text_from_schema(self, heading_schema: Dict[str, Any], index: int) -> Optional[str]:
|
||||
"""
|
||||
Extract actual heading text from schema enum constraints for outline mode.
|
||||
|
||||
Args:
|
||||
heading_schema: The schema definition for a heading level
|
||||
index: The index of the heading (0-based)
|
||||
|
||||
Returns:
|
||||
Actual heading text if found in enum constraints, None otherwise
|
||||
"""
|
||||
# Navigate through the schema structure to find enum constraints
|
||||
# Schema structure: heading_schema -> items -> properties -> content -> enum
|
||||
items_schema = heading_schema.get('items', {})
|
||||
if isinstance(items_schema, dict):
|
||||
properties = items_schema.get('properties', {})
|
||||
if isinstance(properties, dict):
|
||||
content_schema = properties.get('content', {})
|
||||
if isinstance(content_schema, dict):
|
||||
enum_values = content_schema.get('enum', [])
|
||||
if isinstance(enum_values, list) and 0 <= index < len(enum_values):
|
||||
return enum_values[index]
|
||||
|
||||
return None
|
||||
Reference in New Issue
Block a user