feat(skill): add "Setting the narrative?" lens for company/product topics

Comparison tables gain a narrative axis judging whether each entity's
community conversation is about what the entity pitches or about
something else (pricing, rivals, an incident). Backed by a new
mandatory Step 0.55 research item that fetches each entity's CURRENT
first-party positioning (RESOLVED_POSITIONING) instead of relying on
memory, and a narrative-check synthesis beat for single-entity company
runs. The mismatch is the signal: companies usually don't control
their own conversation.
This commit is contained in:
Trevin Chow
2026-06-09 16:24:07 -07:00
parent 122158415a
commit fd0e47d99f
3 changed files with 36 additions and 9 deletions
+23 -6
View File
@@ -531,9 +531,12 @@ def _render_comparison_scaffold(topic: str) -> list[str]:
Returns empty list if topic is not a comparison query. When present,
the block is bracketed so the synthesizer can detect it and pass through.
Axes match the April 9 launch-video exemplar (9 axes suited to AI-tool
comparisons). For non-AI-tool comparisons, the synthesizer writes N/A
or topic-appropriate substitutes in irrelevant rows.
Axes are the 9 from the April 9 launch-video exemplar (suited to AI-tool
comparisons) plus "Setting the narrative?", which asks whether each entity's
community conversation is about what the entity itself pitches, or about
something else (pricing, rivals, incidents). For comparisons where an axis
does not apply, the synthesizer writes N/A or a topic-appropriate substitute
in that row.
"""
entities = _parse_comparison_entities(topic)
if not entities:
@@ -543,10 +546,11 @@ def _render_comparison_scaffold(topic: str) -> list[str]:
header = "| Dimension | " + " | ".join(entities) + " |"
# Separator row matching column count
separator = "|" + "|".join(["---"] * (len(entities) + 1)) + "|"
# 9 axes from the April 9 exemplar. Model fills with topic-appropriate
# content; irrelevant axes get "N/A" rather than invented data.
# 9 axes from the April 9 exemplar plus "Setting the narrative?" (pitch vs.
# what the community actually talks about). See the function docstring.
axes = [
"What it is",
"Setting the narrative?",
"GitHub stars",
"Philosophy",
"Skills",
@@ -558,10 +562,23 @@ def _render_comparison_scaffold(topic: str) -> list[str]:
]
body = [f"| {axis} | " + " | ".join([" "] * len(entities)) + " |" for axis in axes]
# Generic fill rules plus guidance for "Setting the narrative?" - the one
# axis that needs a judgement, not a lookup.
fill_instructions = (
"Fill each cell based on the research above. Keep cells short (5-15 words). "
"Use ' - ' (hyphen with spaces) not em-dashes. Write N/A for axes that do not apply to this topic class. "
"For the \"Setting the narrative?\" row, judge whether each entity's community conversation is about "
"what the entity itself pitches: start the cell with Yes / Partly / No / Unclear, then name the topic "
"the community is ACTUALLY on, anchored to a real item (e.g. \"No - pitches uptime, but the top thread "
"is friendly-fraud (323pt HN)\"). Use Unclear when evidence is thin or polluted with unrelated "
"brand-name matches; do NOT infer a verdict from vibes. Write N/A for entities with no public pitch "
"(people, abstract concepts). This scaffold matches the April 9 launch-video exemplar shape."
)
return [
"## Head-to-Head",
"",
"Fill each cell based on the research above. Keep cells short (5-15 words). Use ' - ' (hyphen with spaces) not em-dashes. Write N/A for axes that do not apply to this topic class. This scaffold matches the April 9 launch-video exemplar shape.",
fill_instructions,
"",
header,
separator,