Dimension-Grouped Questions for 12Ps Framework
Fixed a critical issue where Perplexity was returning garbage/meta-commentary for non-standard section names like "Opening" and "Organization" in the 12Ps outline.
Changelog - 2025-12-06 (#1)
Dimension-Grouped Questions for 12Ps Framework
Overview
Fixed a critical issue where Perplexity was returning garbage/meta-commentary for non-standard section names like "Opening" and "Organization" in the 12Ps outline. The root cause was that guiding questions were passed as a flat list without context about which dimension each question belonged to.
Problem
When the perplexity_section_researcher processed sections with non-standard names (like "Opening" instead of "Market Context"), Perplexity returned responses like:
"Let me fetch the Stratosphere company website and search for more specific market data:"Or:
"I notice that you've asked me to add citations to an 'Opening section' but you haven't
provided the actual text content of that section..."Root cause: The outline YAML had rich dimension-grouped structure:
guiding_questions:
# Problem (P4)
- "How many target customers exist?"
# Possibility (P5)
- "What is the TAM?"But the YAML comments (# Problem (P4)) were lost during parsing, so Perplexity received a flat list with no context about what "Opening" or "Organization" meant.
Solution: Dimension-Grouped Questions
1. Schema Update (src/schemas/outline_schema.py)
Added new dataclass for dimension-organized questions:
@dataclass
class DimensionQuestions:
"""Questions organized by dimension for 12Ps framework."""
dimension: str # e.g., "problem", "possibility", "positioning"
dimension_label: Optional[str] = None # e.g., "Problem (P4)"
questions: List[str] = field(default_factory=list)Added field to SectionDefinition:
questions_by_dimension: Optional[Dict[str, DimensionQuestions]] = None2. Loader Update (src/outline_loader.py)
Added parse_questions_by_dimension() function to parse the new YAML structure.
Updated parse_section() to:
Parse
questions_by_dimensionwhen presentGenerate flat
guiding_questionsfrom dimensions for backward compatibility
3. Researcher Update (src/agents/perplexity_section_researcher.py)
Updated build_section_research_query() to format questions by dimension:
if section_def.questions_by_dimension:
for dim_key, dim_q in section_def.questions_by_dimension.items():
label = dim_q.dimension_label or dim_key.title()
questions_text += f"\n### {label}\n"
for q in dim_q.questions:
questions_text += f"- {q}\n"Also added:
Section description extraction
Group question extraction (e.g., "How big is this problem?")
Structure template extraction
Required elements extraction
Garbage response detection with auto-retry
4. Outline YAML Restructure
Converted sections 2, 3, 4, 6 in direct-early-stage-12Ps.yaml from:
guiding_questions:
# Problem (P4)
- "How many target customers exist?"
- "How is the market experiencing this pain?"To:
questions_by_dimension:
problem:
label: "Problem (P4)"
questions:
- "How many target customers exist?"
- "How is the market experiencing this pain?"
possibility:
label: "Possibility (P5)"
questions:
- "What is the TAM?"
- "What is the SAM?"Updated all three firm copies: dark-matter, hypernova, emerge.
Results
Before (Garbage Responses)
| Section | File Size | Content |
| Opening | 87 bytes | "Let me fetch the Stratosphere company website..." |
| Organization | 109 bytes | "Let me fetch the company website..." |
| Risks | 132 bytes | Meta-commentary asking for clarification |
After (Rich Research Content)
| Section | Words | Citations | Content Quality |
| Opening | 2,011 | 14 | Problem/Possibility/Positioning with TAM/SAM/SOM, competitor analysis |
| Organization | 1,666 | 8 | People/Process/Product with team gaps, engineering culture |
| Risks | 1,696 | 11 | Structured risk analysis by dimension group |
Sample Output (Opening Section)
Now includes:
Problem (P4): Market size $17.9B, 1.17M firefighters, CAGR projections
Possibility (P5): TAM $1.5-3B, SAM $60-100M, SOM $3-10M ARR
Positioning (P6): Competitive analysis vs Hypoxico, Mile High, Loxy
Scorecard table with dimension scores and rationale
What Could Make These Scores Higher section
Additional Fix: Brand Config Loading
Fixed BrandConfig.load() in src/branding.py to use firm name as default brand when firm is provided but brand_name isn't.
Before: Auto-detected firm dark-matter but loaded default Hypernova branding
After: Auto-detected firm dark-matter and loads brand-dark-matter-config.yaml
Files Changed
| File | Change |
src/schemas/outline_schema.py | Added DimensionQuestions dataclass, added fields to SectionDefinition |
src/outline_loader.py | Added parse_questions_by_dimension(), updated parse_section() |
src/agents/perplexity_section_researcher.py | Dimension-grouped formatting, garbage detection, retry logic |
src/branding.py | Fixed firm-scoped brand config loading |
io/dark-matter/templates/outlines/direct-early-stage-12Ps.yaml | Restructured to questions_by_dimension |
io/hypernova/templates/outlines/direct-early-stage-12Ps.yaml | Synced with dark-matter |
io/emerge/templates/outlines/direct-early-stage-12Ps.yaml | Synced with dark-matter |
Impact
This change transforms the 12Ps outline from producing garbage for non-standard section names to producing high-quality, dimension-organized research that matches the framework's intent. The dimension labels (e.g., "Problem (P4)") give Perplexity clear context about what each set of questions is trying to answer.