|
| 1 | +# Content Brief Schema |
| 2 | + |
| 3 | +Every content task requires a completed brief. No brief = no write. |
| 4 | +Save to `/tmp/harness_brief.json` before passing to write agent. |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +## Required Fields |
| 9 | + |
| 10 | +```json |
| 11 | +{ |
| 12 | + "target_site": "kaicalls | buildwithkai | abp | meetkai | connorgallic | vocalscribe", |
| 13 | + "target_keyword": "primary keyword exactly as it appears in title", |
| 14 | + "secondary_keywords": ["2-3 supporting terms"], |
| 15 | + "format": "blog | linkedin | email | tiktok | ad | press", |
| 16 | + "persona": "archetype name from knowledge/personas/", |
| 17 | + "persona_evidence_status": "evidence_backed | directional | hypothesis", |
| 18 | + "persona_evidence_sources": [ |
| 19 | + { |
| 20 | + "source_type": "interview | sales_call | support_ticket | review | survey | analytics | third_party_research | hypothesis", |
| 21 | + "source": "URL, file path, CRM note, or research artifact", |
| 22 | + "retrieved_or_observed_date": "YYYY-MM-DD", |
| 23 | + "confidence": "high | medium | low" |
| 24 | + } |
| 25 | + ], |
| 26 | + |
| 27 | + "current_rank": "position for target keyword, or not ranking", |
| 28 | + "monthly_impressions": 0, |
| 29 | + "current_ctr": 0.0, |
| 30 | + "data_provenance_mode": "sales_external | onboarding_connected | internal_demo | not_applicable", |
| 31 | + "quantitative_claims": [ |
| 32 | + { |
| 33 | + "claim": "exact claim that may appear in output", |
| 34 | + "source": "URL, file path, collector output, or internal measurement", |
| 35 | + "retrieved_or_observed_date": "YYYY-MM-DD", |
| 36 | + "evidence_tier": "official_requirement | official_best_practice | law_regulation_court_status | academic_study | vendor_platform_study | practitioner_benchmark | internal_measurement | inference_hypothesis | missing_data", |
| 37 | + "confidence": "high | medium | low" |
| 38 | + } |
| 39 | + ], |
| 40 | + "data_gaps": ["known missing data that must not be guessed"], |
| 41 | + "competitor_url": "top-ranking URL we're competing against", |
| 42 | + "competitor_weakness": "specific gap in their content — not vague", |
| 43 | + |
| 44 | + "angle": "specific frame — not AI for law firms, but why law firms lose 40% of leads after 5pm", |
| 45 | + "hook_options": [ |
| 46 | + "Hook variant 1", |
| 47 | + "Hook variant 2", |
| 48 | + "Hook variant 3" |
| 49 | + ], |
| 50 | + "audience_pain": "the single biggest frustration of this persona", |
| 51 | + "proof_available": "data, stories, or examples we can use", |
| 52 | + "proof_inventory": [ |
| 53 | + { |
| 54 | + "proof_type": "customer_quote | product_data | case_study | demo | review | benchmark | legal_requirement | platform_requirement", |
| 55 | + "source": "URL, file path, collector output, or note", |
| 56 | + "claim_allowed": "what this proof can safely support" |
| 57 | + } |
| 58 | + ], |
| 59 | + "cta": "what we want them to do after reading", |
| 60 | + |
| 61 | + "ad_concept_bench": { |
| 62 | + "use_when": "required for paid ad batches; omit for non-ad content", |
| 63 | + "personas": ["3-5 situational or psychographic personas"], |
| 64 | + "desires": ["3-5 customer-language desired outcomes"], |
| 65 | + "angles": ["4-8 story, proof, or mechanism frames"], |
| 66 | + "concept_math": "personas x desires x angles = total possible concepts", |
| 67 | + "portfolio_rule": "60% winners, 30% adjacent tests, 10% experiments", |
| 68 | + "selected_concepts": [ |
| 69 | + { |
| 70 | + "concept_id": "PDA-persona-desire-angle-01", |
| 71 | + "persona": "who this ad speaks to", |
| 72 | + "desire": "the progress they want", |
| 73 | + "angle": "the story/proof/frame", |
| 74 | + "awareness_stage": "unaware | problem-aware | solution-aware | product-aware | most-aware", |
| 75 | + "format": "video | static | carousel | UGC | founder | demo", |
| 76 | + "hook": "first line or first 3 seconds", |
| 77 | + "portfolio_bucket": "winner | adjacent | experiment", |
| 78 | + "kill_rule": "named threshold before launch" |
| 79 | + } |
| 80 | + ] |
| 81 | + }, |
| 82 | + |
| 83 | + "word_count_target": 1400, |
| 84 | + "publish_date": "YYYY-MM-DD", |
| 85 | + "internal_links": [ |
| 86 | + "https://site.com/existing-post-1", |
| 87 | + "https://site.com/existing-post-2" |
| 88 | + ] |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +--- |
| 93 | + |
| 94 | +## Word Count Targets by Format |
| 95 | + |
| 96 | +| Format | Target | |
| 97 | +|--------|--------| |
| 98 | +| Blog post | 1200–1800 | |
| 99 | +| LinkedIn article | 700–1000 | |
| 100 | +| Email | 300–500 | |
| 101 | +| TikTok script | 150–300 (spoken words) | |
| 102 | +| Meta ad | 50–150 | |
| 103 | +| Press release | 400–600 | |
| 104 | + |
| 105 | +--- |
| 106 | + |
| 107 | +## Persona Reference |
| 108 | + |
| 109 | +Load the matching file from `knowledge/personas/` before completing the brief. |
| 110 | +Available archetypes: check `ls knowledge/personas/` for current list. |
| 111 | + |
| 112 | +Personas must be labeled: |
| 113 | + |
| 114 | +- `evidence_backed`: supported by interviews, sales calls, support tickets, analytics, reviews, or credible research. |
| 115 | +- `directional`: supported by partial evidence but still needs validation. |
| 116 | +- `hypothesis`: useful for creative exploration only; not enough for client-facing strategy claims. |
| 117 | + |
| 118 | +Do not present a persona pain, budget authority, buying trigger, or objection as fact unless the evidence source is listed. |
| 119 | + |
| 120 | +--- |
| 121 | + |
| 122 | +## Outcome Declaration (ECO O1 — required before writing) |
| 123 | + |
| 124 | +A baseline recorded after the piece ships is not a baseline, and a threshold chosen after seeing the result is not a threshold. Capture both here, before the first draft. |
| 125 | + |
| 126 | +```yaml |
| 127 | +outcome: |
| 128 | + metric: organic_clicks # what this piece is supposed to move |
| 129 | + source: google_search_console # the authoritative system, not a dashboard screenshot |
| 130 | + baseline: 0 # the pre-state, measured now |
| 131 | + threshold: 120 # the number that counts as success |
| 132 | + window_days: 30 # when it gets read |
| 133 | + owner: Connor # who reads it |
| 134 | + attribution: observational # observational | control | holdout | geo_split | switchback |
| 135 | +``` |
| 136 | +
|
| 137 | +This block becomes the `outcome_baseline` evidence entry on the piece's ECO record. Without it the work can reach SHIPPED but never CLOSED. |
| 138 | + |
| 139 | +Floors per format: `harness/eco-floors.yaml` · Doctrine: `docs/system/eco-completion-standard.md` |
| 140 | + |
| 141 | +--- |
| 142 | + |
| 143 | +## Brief Validation |
| 144 | + |
| 145 | +Before write agent starts, validate: |
| 146 | +- `outcome` block is complete (metric, source, baseline, threshold, window, owner) and recorded before drafting |
| 147 | +- All required fields present and non-empty |
| 148 | +- `hook_options` has exactly 3 variants |
| 149 | +- `competitor_weakness` is specific (≥20 words), not generic |
| 150 | +- `angle` is differentiated from `target_keyword` (not just a restatement) |
| 151 | +- `proof_available` references actual data or a named example |
| 152 | +- `persona_evidence_status` is present and matches the sources |
| 153 | +- Every quantitative claim has a source, evidence tier, date, and confidence label |
| 154 | +- Missing data is listed in `data_gaps`, not replaced with a benchmark |
0 commit comments