Navigable Artifacts
Selected prompt excerpts that demonstrate editorial reasoning encoded in the system. Each excerpt shows a specific architectural decision about how the agents handle content.
COMPOSITION_RULES
agent_draft.pyThis prompt inverts the typical synthesis-suggestive / draft-authoritative hierarchy. The "narrative_payloads are draft-ready prose, not rough material to rewrite" rule means the comparison agent does the heavy editorial lifting on tensions, and the draft agent writes connective tissue around it.
View prompt
# Composition You are composing a complete comparison piece for the Vercel knowledge base. It should read as a sibling to vercel-vs-fastly and vercel-waf-vs-cloudflare-waf — confidently Vercel-leaning, structurally fair, written for developers who are choosing. You have three inputs: - The COMPARISON MATRIX (JSON, structured editorial spec) - The COMPARISON BRIEF (markdown, the matrix rendered for humans — same content, easier to scan) - The editorial context above (shared_rules.md + piece_brief.md), which gives you universal principles and per-piece specifics Your job is to interpret these inputs into prose. Not transcription, not rendering — interpretation. The matrix IS the editorial spec; the brief IS the reading guide; piece_brief.md IS the structural reference. Trust them. ## Four constraints that matter — with reasoning 1. **`narrative_payload` fields in the matrix are draft-ready prose.** They were generated under the same voice register as the rest of the piece (shared_rules.md §4 + piece_brief.md voice). When the matrix has a tension with a `narrative_payload`, insert it at the location its `draft_placement` field indicates — verbatim or near-verbatim. They are not rough material to rewrite. 2. **Tables earn their place.** Use them where they communicate more cleanly than prose: capability comparisons, the pricing comparison, and the "When to choose" decision table. 3. **Verdicts spine the "When to choose" section.** Each dimension's `verdict.rationale` and `reader_decision_impact.reasoning` is the source for one or more rows in the decision table. 4. **Temporal goes once, in the lead, parenthetically.** The `category_framing` entry for Temporal exists to acknowledge category vocabulary, not to expand into a third subject. ## Voice — why, not just what You're writing for developers who read engineering blogs, not marketing pages. They trust voices that name specific tradeoffs over voices that promise everything works seamlessly. ## Output Produce the complete piece in markdown. No preamble, no meta-commentary, no JSON wrapper. Just the article — title through closing CTA.
REVISION_RULES
agent_draft.pyThe reader test is built as an accountability surface, not as an in-prompt rubric the model would mirror back as section headings. Three concrete questions a developer should be able to answer after reading.
View prompt
# Revision
You just composed the draft above. Now revise it.
Your job in this pass is editorial self-review, not rewriting from scratch. Read the draft once with the reader test in mind. Read it again with the anti-pattern list in mind. Note what would change and why. Then produce the revised version.
## Reader test (actionable form, from shared_rules.md §9)
A developer reading the piece should come away able to:
1. Make a decision about which product fits their workload.
2. Explain the architectural difference between the products in their own words.
3. Articulate at least one thing the non-commissioning product (Cloudflare) does better than the commissioning one (Vercel).
If your honest read says the draft fails any of these, revise the relevant section before final output.
## Anti-pattern check
Review against the universal anti-patterns enumerated in shared_rules.md §8:
- False balance ("both products have tradeoffs")
- Universal winner ("Product X is better")
- Marketing copy with attribution
- Feature inventory disguised as comparison
- Hedging that dissolves real differences
- Burying the architectural fork
- Ignoring the category authority
## Output format
Output exactly this two-section format:
=== REVISION_NOTES ===
[Specific notes on what you found in self-review and what you changed.]
=== FINAL_DRAFT ===
[The revised complete piece in markdown. Title through closing CTA.]EXTRACTION_RULES
agent_extract.pySchema-driven extraction where source type is inferred from the source's role rather than branched on hardcoded JSON keys. The note "extract from the page CONTENT, not from claim_summary" is a guardrail against a common failure mode.
View prompt
# Extraction rules
You are extracting claims from a single web source for a competitive comparison article. The editorial context above governs WHY claims matter; these rules govern HOW to extract them.
You will receive:
- The source's metadata (role, vendor, dimensions it covers, optional claim_summary previewing what to look for, optional disputed_claims this source takes positions on)
- Handling rules from `roles.json` (claim_handling, extraction_depth, quotable, always_attribute)
- The fetched page content
Return a JSON object of this exact shape:
{
"discovered_published_date": "<YYYY-MM-DD if a publication date is visibly stated on the page; otherwise null>",
"claims": [
{
"claim": "<one verifiable fact or position, in plain English, phrased per the claim_handling rule>",
"verbatim_quote": "<short direct quote supporting the claim, only if quotable=true and a quote is available; otherwise null>",
"dimensions_covered": ["<subset of the source's dimensions_covered that THIS specific claim addresses; do not just copy the source's full list>"],
"disputed_claims_ref": ["<from the source's disputed_claims list if this claim addresses one of them; otherwise empty array>"]
}
]
}
Universal rules:
- Only emit what the source actually says. Do NOT invent. Do NOT extrapolate.
- Use claim_summary as a hint about what to look for, but extract from the page CONTENT, not from claim_summary.
- Each claim must be discrete and verifiable from the source content.
- Skip vague developer-marketing phrases unless quantified.
- Output ONLY valid JSON. No prose before or after.CLAIM_HANDLING_GUIDANCE
agent_extract.pyFive distinct rhetorical postures the system can apply to any claim. The separation of factual_with_attribution from partisan_critique is the editorial decision that lets vendor docs be quoted as fact while competitor critiques get explicit attribution.
View prompt
CLAIM_HANDLING_GUIDANCE = {
"factual_authoritative": (
"State as fact. No hedging. The source is authoritative for these claims."
),
"factual_with_attribution": (
"State as the vendor's claim. Use phrasing like 'Vercel says X' or "
"'according to Cloudflare's docs'. The reader should know this is the "
"vendor's own positioning, not an independent assessment."
),
"partisan_critique": (
"State as the source's argument with explicit attribution baked into the "
"claim text. Do NOT state as fact. Use phrasing like 'Inngest argues that X' "
"or 'Per Inngest's blog, Y'. The reader must understand this is the source's "
"position, not a neutral truth."
),
"category_authority": (
"State as the framework or definition the source establishes for the broader "
"category. Useful for vocabulary and taxonomy, not for product-specific facts."
),
"community_signal": (
"Emit ONLY thematic summaries of what the community is discussing. Do NOT "
"extract individual commenter quotes or attribute claims to specific users. "
"Each claim should be a thematic observation like 'Developers expressed "
"concern about X' or 'The thread surfaces tension between Y and Z'. The "
"output is evidence that a debate exists, not what specific people said."
),
}_SELECT_VOICE_ANCHOR( )
prompt_context.pyVoice samples are matched to pieces through metadata. Selection happens deterministically: exact match on genre and subject domain wins, with primary_voice_anchor: true as universal fallback.
View prompt
def _select_voice_anchor(brief_anchor: dict, style_refs: list) -> dict:
"""
Select the best-matching style_reference per the rules locked in step 1:
- Exact match on genre AND subject_domain wins.
- If multiple matches: primary_voice_anchor=true is the tiebreaker;
otherwise first in array order.
- If zero matches: fall back to primary_voice_anchor=true (universal
fallback). Closest-but-weak is worse than the explicit primary.
"""
primaries = [r for r in style_refs if r.get("primary_voice_anchor") is True]
if len(primaries) > 1:
raise ValueError(
f"Schema violation: {len(primaries)} style_references have "
"primary_voice_anchor=true; expected exactly one."
)
primary = primaries[0] if primaries else None
genre = brief_anchor.get("genre")
subject_domain = brief_anchor.get("subject_domain")
matches = [
r for r in style_refs
if r.get("genre") == genre and r.get("subject_domain") == subject_domain
]
if matches:
if len(matches) == 1:
return matches[0]
primary_in_matches = [r for r in matches if r.get("primary_voice_anchor")]
return primary_in_matches[0] if primary_in_matches else matches[0]
# Zero matches → fall back to primary (universal tiebreaker)
if primary is None:
raise ValueError(
f"No style_reference matches genre={genre!r} subject_domain={subject_domain!r}, "
"and no primary_voice_anchor is set. Cannot select a voice anchor."
)
return primaryTENSION_RULES
agent_compare.pyThe architectural centerpiece of the comparison agent. The constraint that "narrative_payload WILL APPEAR in the final piece" inverts the typical synthesis-suggestive / draft-authoritative hierarchy.
View prompt
# Tension composition
Compose a single editorial tension entry. Return JSON of this shape:
{
"subject_a_position": "<...>" | null,
"subject_a_steelman": "<charitable case for subject_a's choice>" | null,
"subject_b_position": "<...>" | null,
"subject_b_steelman": "<charitable case for subject_b's choice>" | null,
"disclosing_subject": "subject_a" | "subject_b" | null,
"disclosure": "<what the subject discloses>" | null,
"external_voice": "<vendor name>" | null,
"critique_summary": "<the critique>" | null,
"narrative_payload": "<3-5 sentence draft-ready paragraph>",
"draft_placement": "<where this paragraph belongs in the piece>",
"steelman_quality": "both_strong" | "asymmetric" | "weak",
"reader_decision_impact": {
"score": 0 | 1 | 2 | 3,
"reasoning": "<one sentence>"
}
}
Type-specific population:
- design_disagreement_between_subjects: populate the four subject_*/steelman fields. Leave disclosure/external fields null.
- subject_disclosure: populate disclosing_subject + disclosure. Leave subject_*/external fields null.
- external_critique: populate external_voice + critique_summary. Leave subject_*/disclosure null.
NARRATIVE_PAYLOAD constraints (CRITICAL):
- 3 to 5 sentences. No more, no less.
- Voice and register per shared_rules.md §4 AND piece_brief.md §Voice register.
- This paragraph WILL APPEAR in the final piece. Write it as draft prose, not as analysis ABOUT the tension.
- The draft agent will insert this verbatim or with minimal edits and write only connective tissue around it.
- For two-sided tensions: present both positions with charitable framing. Do not resolve.
Output ONLY valid JSON.