@jairodri/huckleberry-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/agents/cos.json +20 -0
  2. package/agents/hermes/AGENTS.md +62 -0
  3. package/agents/hermes/SOUL.md +29 -0
  4. package/agents/hermes/fix-kimaki/SKILL.md +100 -0
  5. package/agents/hermes/hermes-fixer.md +115 -0
  6. package/agents/hermes.json +26 -0
  7. package/bin/huckleberry.js +4 -0
  8. package/kanban/SKILL.md +167 -0
  9. package/kanban/kanban +3 -0
  10. package/kanban/package.json +9 -0
  11. package/kanban/src/cli.mjs +553 -0
  12. package/kanban/src/dispatcher.mjs +479 -0
  13. package/kanban/src/server.mjs +108 -0
  14. package/kanban/templates/b2b-outreach.yaml +84 -0
  15. package/kanban/templates/blog-social-campaign.yaml +1161 -0
  16. package/kanban/templates/client-onboard.yaml +107 -0
  17. package/kanban/templates/cro-pipeline.yaml +296 -0
  18. package/kanban/templates/social-campaign.yaml +356 -0
  19. package/mcp/gemini-codegen-server.mjs +134 -0
  20. package/package.json +46 -0
  21. package/plugins/env-inject.ts +61 -0
  22. package/plugins/inkstone-gate.ts +55 -0
  23. package/scripts/heartbeat.sh +41 -0
  24. package/scripts/install-services.ps1 +82 -0
  25. package/scripts/install-services.sh +74 -0
  26. package/scripts/keep-awake.ps1 +48 -0
  27. package/scripts/keep-awake.sh +31 -0
  28. package/scripts/keepalive.sh +43 -0
  29. package/skills/curated.txt +68 -0
  30. package/src/cli.js +227 -0
  31. package/src/interview.js +78 -0
  32. package/src/postinstall.js +22 -0
  33. package/taskmaster/SKILL.md +78 -0
  34. package/taskmaster/dispatcher.mjs +146 -0
  35. package/taskmaster/next-task.js +123 -0
  36. package/taskmaster/update-status.js +118 -0
  37. package/templates/AGENTS.md +90 -0
  38. package/templates/COS_PROTOCOLS.md +71 -0
  39. package/templates/EXECUTIVE-MODE.md +29 -0
  40. package/templates/HEARTBEAT.md +42 -0
  41. package/templates/IDENTITY.md +22 -0
  42. package/templates/MEMORY.md +17 -0
  43. package/templates/ORG.md +33 -0
  44. package/templates/SOUL.md +43 -0
  45. package/templates/TOOLS.md +66 -0
  46. package/templates/business-context-gate.md +9 -0
  47. package/templates/evergreen.md +29 -0
  48. package/templates/opencode.json +80 -0
  49. package/wiki/discord.md +52 -0
  50. package/wiki/inkstone.md +64 -0
  51. package/wiki/kimaki.md +48 -0
  52. package/wiki/opencode.md +43 -0
@@ -0,0 +1,1161 @@
1
+ name: Content Campaign (NLM-Grounded + Full Quality Stack + Learning Loop)
2
+ description: >
3
+ Full content pipeline with NLM-grounded drafts, intent-driven outlines, structural variation,
4
+ SEO-ready images with metas, complete schema (Article+FAQ+Author+Breadcrumb), and a feedback
5
+ loop that learns from every approval/rejection.
6
+ 14-layer pipeline: Load Lessons → Thesis → Research → Intent+Outline → NLM Draft → Hooks →
7
+ Polish → Enrich (images+schema) → Publish → Index → Distribute → Links → Track → Learn.
8
+ NLM writes from a structured intent outline (not freestyle). Harper agent enforces brand voice.
9
+ Every rejection feeds Inkstone + evergreen so the system improves with every article.
10
+ See: workspace/kimaki-kanban/content-quality-standard.md for the full spec.
11
+ Brand-agnostic.
12
+
13
+ variables:
14
+ - name: topic
15
+ required: true
16
+ description: The content topic or keyword focus
17
+ - name: tenant
18
+ required: true
19
+ description: Brand/project tenant name (maps to a kimaki project)
20
+ - name: audience
21
+ default: general
22
+ description: Target audience for the content
23
+ - name: platforms
24
+ default: twitter,linkedin,reddit,medium
25
+ description: Comma-separated list of social platforms
26
+ - name: article_type
27
+ default: standard
28
+ description: "Article type: standard, vs (comparison), alternatives (roundup), storytelling, how_to, guide"
29
+ - name: language
30
+ default: en
31
+ description: "Article language code: en, es, fr, etc."
32
+ - name: funnel_id
33
+ required: false
34
+ description: Funnel ID (A-M) from the brand's funnels/ directory.
35
+ - name: funnel_stage
36
+ default: MOFU
37
+ description: "Funnel stage: TOFU, MOFU, or BOFU. Determines narrative arc and CTA."
38
+ - name: narrative_arc
39
+ required: false
40
+ description: "Override narrative arc. Auto-selected by funnel_stage if not set."
41
+ - name: icp_segment
42
+ required: false
43
+ description: "ICP segment this article targets (e.g. backpackers, car-campers, resort-managers). Drives pain points, language, and CTA."
44
+ - name: cluster_node_id
45
+ required: false
46
+ description: "Node ID in brand/topic-clusters.json. Used for internal linking and status tracking."
47
+
48
+ tasks:
49
+ # ── LAYER -1: LOAD PAST LESSONS ────────────────────────────
50
+
51
+ - id: load_lessons
52
+ title: "Load quality lessons: {{topic}}"
53
+ assignee: build
54
+ skills: []
55
+ priority: 5
56
+ gate: true
57
+ body: |
58
+ Before ANY work on this article, load all past rejection and success patterns
59
+ for this brand. This prevents repeating mistakes and reinforces what works.
60
+ Every downstream task receives the compiled avoidance brief.
61
+
62
+ **1. Query Inkstone for failure patterns:**
63
+ inkstone_memory_failures(query="{{tenant}} article rejection content quality")
64
+ If Inkstone is unavailable, skip gracefully (soft dependency).
65
+
66
+ **2. Query Inkstone for success patterns:**
67
+ inkstone_memory_search(
68
+ query="{{tenant}} article approved what worked",
69
+ namespace="/business/{{tenant}}/content-quality",
70
+ limit=5
71
+ )
72
+
73
+ **3. Read brand evergreen content quality lessons:**
74
+ Read businesses/{{tenant}}/evergreen.md
75
+ Look for a "Content Quality Lessons" section.
76
+
77
+ **4. Compile AVOIDANCE BRIEF (save to content/_drafts/{slug}/avoidance-brief.md):**
78
+ ```
79
+ BRAND: {{tenant}}
80
+ DATE: [today]
81
+
82
+ DO NOT (past rejections):
83
+ - [list each failure pattern with specific fix]
84
+
85
+ DO (past successes):
86
+ - [list each success pattern to reinforce]
87
+
88
+ WATCH FOR:
89
+ - [recurring issues that need monitoring]
90
+ ```
91
+
92
+ **5. This brief is passed to EVERY downstream task.**
93
+ Every subagent sees it. No excuses for repeating past mistakes.
94
+
95
+ # ── LAYER 0: SHARP-THESIS VALIDATION ──────────────────────
96
+
97
+ - id: thesis_validation
98
+ title: "Validate sharp thesis: {{topic}}"
99
+ assignee: build
100
+ skills: [sharp-thesis-validator]
101
+ parents: [load_lessons]
102
+ priority: 4
103
+ gate: true
104
+ body: |
105
+ Validate that "{{topic}}" passes the Sharp-Thesis (Cerebras) Test before any work begins.
106
+ This is Layer 0 — blocks all downstream tasks until PASS.
107
+
108
+ Use the `sharp-thesis-validator` skill (already loaded by dispatcher).
109
+ Pass to the skill: topic="{{topic}}", brand="{{tenant}}", mode="long-form"
110
+
111
+ **On PASS:** Mark complete. Proceed to research.
112
+ **On FAIL:** BLOCK. Present reframing suggestions to user.
113
+ User must approve a reframed topic before proceeding.
114
+ Update the {{topic}} variable with the reframed title.
115
+
116
+ **Criteria checked:**
117
+ 1. Single arguable claim (not "X is important" but "X does Y because Z")
118
+ 2. Concrete number (dollar, percentage, timeframe, count)
119
+ 3. Urgency / why now (time-bound trigger)
120
+ 4. Title expressibility ≤80 chars (or thesis-config override)
121
+
122
+ Brand context loaded from: businesses/*/brand-assets/thesis-config.md (if exists)
123
+ Claims blacklist checked against: brand-overrides.md + landscape.md
124
+
125
+ # ── LAYER 1: INTELLIGENCE ───────────────────────────────────
126
+
127
+ - id: research
128
+ title: "Research: {{topic}}"
129
+ assignee: researcher
130
+ skills: [keyword-research, subreddit-pain-extraction, competitive-intel, audience-research]
131
+ parents: [thesis_validation]
132
+ priority: 3
133
+ body: |
134
+ Research keywords, content gaps, pain points, and audience language for "{{topic}}" targeting {{audience}}.
135
+ This is Layer 1 of the Content Quality Standard. Gather ALL intelligence before NLM drafting.
136
+ Reference: workspace/kimaki-kanban/content-quality-standard.md
137
+
138
+ Call load-business-context with include_funnels: true before starting.
139
+ {{#funnel_id}}
140
+ FUNNEL: This content serves funnel {{funnel_id}}. Read funnels/{{funnel_id}}-*.md for CTA, buyer stage, and ICP segment.
141
+ All research must align with this funnel's conversion path and ICP.{{/funnel_id}}
142
+
143
+ **1. Keyword research + intent classification**
144
+ Run keyword research for search volume, competition data, and SERP gaps.
145
+ Classify each keyword by intent:
146
+ - informational (how to, what is, best) → TOFU
147
+ - evaluation (comparison, review, vs) → MOFU
148
+ - transactional (buy, price, get) → BOFU
149
+ The intent classification determines the article structure (see below).
150
+
151
+ **2. PAA extraction (CRITICAL for AEO version)**
152
+ Extract ALL People Also Ask questions from Google SERPs for the primary keyword.
153
+ Save these verbatim. The AEO version of the article uses these EXACT questions as H2 headings.
154
+ If PAA questions are not available (new/niche keyword), generate 5-7 questions from related searches.
155
+
156
+ **3. Pain point extraction**
157
+ Mine Reddit for pain points around "{{topic}}". Extract verbatim "pain phrases" buyers actually use.
158
+ These phrases go directly into the article and into the NLM prompt.
159
+
160
+ **4. ICP language bank**
161
+ {{#icp_segment}}
162
+ Target ICP: {{icp_segment}}. Build a phrase bank of exact words/phrases
163
+ this specific segment uses to describe their problem.
164
+ {{/icp_segment}}
165
+ {{^icp_segment}}
166
+ Build a phrase bank of exact words/phrases the target audience uses.
167
+ {{/icp_segment}}
168
+
169
+ **5. Competitor gap analysis**
170
+ Check who ranks, what angles they take, what they miss.
171
+ Identify the content gap this article fills.
172
+
173
+ **6. Article structure guidance**
174
+ Based on funnel stage + keyword intent, determine the article structure:
175
+ - TOFU + informational → Educational explainer (what is X, how X works)
176
+ - TOFU + problem-aware → Problem-solution (pain → mechanism → resolution)
177
+ - MOFU + evaluation → Balanced comparison (criteria → evaluate → recommend)
178
+ - BOFU + transactional → Decision guide (use case → options → pick)
179
+ - BOFU + comparison → Head-to-head (overview → criteria → feature-by-feature → verdict)
180
+ The structure guides section headings and flow but MUST NOT inject framework labels
181
+ (like "The Problem", "The Agitation", "Attention:", "Desire:") into the content itself.
182
+ Content should read as natural educational prose, not formulaic sales copy.
183
+
184
+ **7. Topic cluster lookup**
185
+ Read brand/topic-clusters.json if it exists. Find the node matching this topic
186
+ (or create a new one). Note:
187
+ - Parent node (for WP category assignment and internal linking)
188
+ - Child nodes (for deep-dive candidates)
189
+ - Sibling nodes (for related content links)
190
+ {{#cluster_node_id}}
191
+ Cluster node ID: {{cluster_node_id}}. Update status to "in_progress" in topic-clusters.json.
192
+ {{/cluster_node_id}}
193
+
194
+ **8. Internal link map**
195
+ Read brand/link-map.json. Note which pages this article should link to
196
+ and which pages should link back to this article.
197
+
198
+ Produce a combined research brief with:
199
+ - Keywords with intent classification
200
+ - PAA questions (verbatim, for AEO structure)
201
+ - Pain phrases (verbatim, for article body)
202
+ - ICP language bank
203
+ - Competitive gaps
204
+ - Selected article structure + explanation
205
+ - Funnel metadata (stage, CTA, arc)
206
+ - Topic cluster node info (parent, children, siblings)
207
+ - Internal link targets
208
+ Save output to the brand's content directory.
209
+
210
+ # ── LAYER 1.5: INTENT + NLM OUTLINE ─────────────────────────
211
+
212
+ - id: intent_outline
213
+ title: "Intent + NLM outline: {{topic}}"
214
+ assignee: harper
215
+ skills: [nlm-cli, seo-content]
216
+ parents: [research]
217
+ priority: 3
218
+ deliverables: "businesses/{{tenant}}/content/_drafts/{{topic_slug}}/intent-outline.md|500"
219
+ body: |
220
+ Infer search intent from the keyword and build a structured NLM outline
221
+ that DIRECTLY ANSWERS that intent. This outline is the blueprint for the NLM draft.
222
+ Without this step, NLM writes freestyle and misses what the searcher actually wants.
223
+
224
+ Load the nlm-cli skill for NLM concepts. Use the notebooklm CLI (notebooklm-py v0.7.3)
225
+ for all commands. Run `notebooklm doctor` to verify auth. Use the notebook from research.
226
+
227
+ **1. CLASSIFY SEARCH INTENT from the primary keyword:**
228
+ Read the research brief. Determine the user's actual goal:
229
+ - Informational ("what is", "how does") → wants to understand a concept
230
+ - Problem-solving ("how to fix", "why does") → has a specific pain needing resolution
231
+ - Comparison ("X vs Y", "best X for") → choosing between options
232
+ - Transactional ("buy", "cost", "hire") → ready to take action
233
+ - Navigational ("login", "dashboard") → looking for a specific resource
234
+
235
+ State the inferred intent explicitly:
236
+ "The user searching '[PRIMARY KEYWORD]' wants: [INFERRED NEED]"
237
+
238
+ **2. QUERY NLM for a structured article OUTLINE (not the article):**
239
+
240
+ ```
241
+ notebooklm generate report --format custom --wait --json \
242
+ "A user searched: '[PRIMARY KEYWORD]'
243
+ Their intent is: [CLASSIFIED INTENT from step 1]
244
+ What they actually want to know: [INFERRED NEED from step 1]
245
+
246
+ Create a detailed article OUTLINE (NOT the article itself) that:
247
+ - Opens by acknowledging their specific situation or pain
248
+ - Each H2 section answers a distinct sub-question they have
249
+ - Flows logically: context → problem → understanding → solution → action
250
+ - Includes these PAA questions as H2 sections: [insert PAA questions from research]
251
+ - Identifies where data, examples, frameworks, or visuals are needed
252
+ - Marks which sections need AEO-style concise answer blocks
253
+
254
+ STRUCTURAL VARIATION IS MANDATORY in the outline:
255
+ - NOT every H2 section should be the same depth. Vary deliberately.
256
+ Some sections are 1-2 paragraphs. Others are 4-5 paragraphs.
257
+ One section might be a single punchy callout.
258
+ - Mark the expected depth for each section (short/medium/long).
259
+ - This variation creates natural reading rhythm. Uniform sections are artificial.
260
+
261
+ For EACH section in the outline, specify:
262
+ - H2 heading (in the user's language, not industry jargon)
263
+ - What question this section answers
264
+ - Key points to cover (grounded in your sources)
265
+ - Expected depth: short (1-2 para), medium (3-4 para), or long (5+ para)
266
+ - Whether this section needs an AEO answer block (concise citable answer)
267
+ - Any data points or examples to include"
268
+ ```
269
+
270
+ **3. SAVE the outline:**
271
+ businesses/{{tenant}}/content/_drafts/{slug}/intent-outline.md
272
+
273
+ This outline is the BLUEPRINT. The NLM draft in Layer 2 follows it section by section.
274
+ The structural variation markers (short/medium/long) ensure the article reads naturally.
275
+
276
+ # ── LAYER 2: NLM GROUNDING ────────────────────────────────
277
+
278
+ - id: nlm_draft
279
+ title: "NLM draft: {{topic}}"
280
+ assignee: harper
281
+ skills: [nlm-cli]
282
+ parents: [intent_outline]
283
+ priority: 3
284
+ deliverables: "businesses/{{tenant}}/content/_drafts/{{topic_slug}}/narrative-draft.md|5000;businesses/{{tenant}}/content/_drafts/{{topic_slug}}/aeo-draft.md|2000"
285
+ body: |
286
+ Use NLM to write a grounded article draft from real expert sources.
287
+ NLM produces raw content. Our skills layer (hooks, voice DNA, humanizer) shapes it later.
288
+
289
+ Use the notebooklm CLI (notebooklm-py v0.7.3). Run `notebooklm doctor` to verify auth.
290
+
291
+ **1. Find and add sources**
292
+ Find relevant sources for "{{topic}}":
293
+ - Search YouTube for expert videos on the topic
294
+ - Search for podcast episodes (Podscan API or web search)
295
+ - Find authoritative articles and studies
296
+ Target 5-10 quality sources.
297
+
298
+ **2. Create or reuse NLM notebook**
299
+ Check if a notebook exists for this cluster (read brand/topic-clusters.json for notebook_id).
300
+ If not, create one:
301
+ ```
302
+ notebooklm create "{{tenant}}: {{topic}}" --use
303
+ ```
304
+ Add all sources (type is auto-detected):
305
+ ```
306
+ notebooklm source add "https://youtube.com/watch?v=..."
307
+ notebooklm source add "https://example.com/article"
308
+ notebooklm source add "path/to/transcript.txt"
309
+ ```
310
+ Wait 30-60 seconds for indexing after adding sources.
311
+ Save notebook_id to brand/topic-clusters.json.
312
+
313
+ **3. Generate or update mindmap**
314
+ Run mindmap creation to extract topic clusters:
315
+ ```
316
+ notebooklm generate mind-map --instructions "{{topic}} clusters" --json
317
+ ```
318
+ Parse the mindmap output. Save new nodes to brand/topic-clusters.json
319
+ (status: not_started, depth_level based on tree position).
320
+
321
+ **4. Query NLM for NARRATIVE version**
322
+ Use --format blog-post with --append to inject quality instructions.
323
+ The built-in blog-post format produces engaging content. Our --append adds structure.
324
+
325
+ CRITICAL: Read the outline from intent-outline.md (Layer 1.5). The --append text MUST
326
+ instruct NLM to follow that outline section by section.
327
+
328
+ ```
329
+ notebooklm generate report --format blog-post --wait --json \
330
+ --append "Target audience: {{audience}}. Article type: {{article_type}}.
331
+
332
+ FOLLOW THIS OUTLINE (from intent-outline.md):
333
+ [Paste the full outline here, section by section]
334
+
335
+ REQUIREMENTS:
336
+ - Primary keyword: [from research brief]
337
+ - Pain points to address: [verbatim pain phrases from research]
338
+ - Narrative arc: {{narrative_arc}}
339
+ - 1500-2500 words
340
+ - Follow the outline structure exactly. Each H2 from the outline becomes a section.
341
+ - Respect the depth markers from the outline (short/medium/long sections).
342
+
343
+ STRUCTURAL VARIATION IS MANDATORY:
344
+ - NOT every H2 section should be the same length. This is the #1 AI writing tell.
345
+ - Some sections are 1-2 paragraphs. Others are 5-6 paragraphs. One might be a single sentence.
346
+ - Vary paragraph lengths too: mix 1-sentence punchy paragraphs with 4-5 sentence deep paragraphs.
347
+ - Use short paragraphs for emphasis and transitions. Use long ones for evidence and depth.
348
+ - No two consecutive paragraphs should be the same length.
349
+ - This creates natural human rhythm. Uniform structure signals AI-generated content.
350
+
351
+ - Include named frameworks and specific data from your sources
352
+ - Include quotable insights and stats with citations
353
+ - End with 5 FAQ questions and answers
354
+ - Language: {{language}}"
355
+ ```
356
+ Save the narrative draft output.
357
+
358
+ **5. Query NLM for AEO version**
359
+ Structure around the PAA questions extracted in research. This version targets AI answer engines.
360
+
361
+ ```
362
+ notebooklm generate report --format custom --wait --json \
363
+ "Create an AEO (Answer Engine Optimization) version about {{topic}}.
364
+ This version is designed to be cited by AI answer engines (ChatGPT, Perplexity, Google AI Overviews).
365
+
366
+ Structure around these EXACT questions from Google People Also Ask:
367
+ [Insert PAA questions verbatim from research brief]
368
+
369
+ For each question:
370
+ - H2 = the exact PAA phrasing (word for word)
371
+ - Answer: 150-300 words, direct and citable
372
+ - Include specific data points and named frameworks from your sources
373
+ - Include quotable stats
374
+ - End with a summary sentence that captures the key takeaway
375
+
376
+ If there are fewer than 5 PAA questions, generate additional questions from your sources.
377
+ Language: {{language}}"
378
+ ```
379
+ Save the AEO draft output.
380
+
381
+ **6. Save outputs**
382
+ Save to the brand's content directory:
383
+ - narrative-draft.md (from NLM narrative query)
384
+ - aeo-draft.md (from NLM AEO query)
385
+ - nlm-sources-used.json (sources NLM cited)
386
+ - nlm-notebook-id.txt (for future deep-dives)
387
+
388
+ These drafts are RAW. The hooks task generates titles, the polish task applies voice DNA and humanizer.
389
+
390
+ # ── LAYER 3: HOOKS (AFTER NLM OUTLINE) ──────────────────────
391
+
392
+ - id: hooks
393
+ title: "Generate hooks: {{topic}}"
394
+ assignee: harper
395
+ skills: [hook-generator]
396
+ parents: [nlm_draft]
397
+ priority: 3
398
+ body: |
399
+ Generate hooks that FIT the NLM-generated outline and incorporate SEO keywords.
400
+ This is Layer 3 of the Content Quality Standard.
401
+ Hooks come AFTER the NLM draft because titles must match the actual content.
402
+
403
+ Load the research brief (keywords, PAA questions, pain phrases) AND the NLM draft.
404
+
405
+ **Tournament mode**: Generate 5+ hook variants, run through 5 parallel quality-gate
406
+ judge personas (skeptic, scanner, buyer, expert, outsider). Winner proceeds to standard quality-gate.
407
+ See: Skill quality-gate tournament mode.
408
+
409
+ Load hook-generator skill and follow its full workflow:
410
+ 1. Read the NLM narrative draft to understand the actual content and angle.
411
+ 2. Feed the research brief as source context (keywords, pain phrases, ICP language).
412
+ 3. Generate hooks that:
413
+ - Match the actual NLM content (not generic to the topic)
414
+ - Incorporate the primary SEO keyword naturally
415
+ - Work as the article title AND the first social post line
416
+ - Create curiosity without clickbait
417
+ 4. Generate across all 6 Sentence Strategies + 6 Formulas with Spice Mixing.
418
+ 5. Include Contrarian Angles where appropriate.
419
+ 6. Platform-match hooks for {{platforms}}.
420
+ 7. Score all options.
421
+
422
+ Present top 3-5 hooks to user in Discord ranked by score.
423
+ **BLOCK this task — do NOT proceed to polish until user picks a hook.**
424
+
425
+ Hook rules: No em dashes. No colons in hooks. Under 15 words preferred.
426
+ The approved hook becomes the article title and first social post line.
427
+ Also generate 2-3 subtitle/subheading variants for the approved hook.
428
+
429
+ # ── LAYER 4: POLISH (REPLACES write + validate) ─────────────
430
+
431
+ - id: polish
432
+ title: "Polish NLM draft: {{topic}}"
433
+ assignee: harper
434
+ skills: [seo-content, blog-publish-pipeline, schema-markup, marketing-psychology, voice-brand-alignment, humanizer, concreteness-fixer, quality-gate]
435
+ parents: [hooks, nlm_draft]
436
+ priority: 2
437
+ deliverables: "businesses/{{tenant}}/content/_drafts/{{topic_slug}}/narrative-draft-polished.md|5000;businesses/{{tenant}}/content/_drafts/{{topic_slug}}/aeo-draft-polished.md|2000"
438
+ body: |
439
+ Polish the NLM-generated draft. Apply hooks, voice DNA, humanizer, and quality gate.
440
+ This is Layer 4 of the Content Quality Standard.
441
+ The agent does NOT write from scratch. The agent refines NLM output.
442
+
443
+ CRITICAL: Output to SEPARATE polished files. Do NOT overwrite the raw NLM drafts.
444
+ - Read: narrative-draft.md (raw NLM output)
445
+ - Write: narrative-draft-polished.md (polished version)
446
+ - Read: aeo-draft.md (raw NLM output)
447
+ - Write: aeo-draft-polished.md (polished version)
448
+ The publish task reads the POLISHED files, not the raw drafts.
449
+
450
+ **Inputs:**
451
+ - NLM narrative draft (from nlm_draft task)
452
+ - NLM AEO draft (from nlm_draft task)
453
+ - Approved hook + subtitles (from hooks task)
454
+ - Research brief (keywords, PAA questions, pain phrases, ICP language)
455
+
456
+ **POLISH SEQUENCE (run in order):**
457
+
458
+ 1. **Apply hooks**
459
+ - Replace NLM title with approved hook
460
+ - Add approved subtitles as H2 where appropriate
461
+ - Ensure the hook works as the opening line (narrative version)
462
+
463
+ 2. **Voice-brand-alignment pass**
464
+ Load voice-brand-alignment skill. Load brand voice DNA from brand/voice-profile.md
465
+ or brand/voice-dna.json (whichever exists).
466
+ Verify the article matches brand voice. Fix any voice drift.
467
+ NLM does not know the brand voice. Our voice DNA system enforces it here.
468
+
469
+ 3. **Humanizer pass**
470
+ Load humanizer skill. Strip ALL 24 AI writing patterns:
471
+ - Content patterns: significance inflation, promotional language, vague attributions, rule of three, generic conclusions
472
+ - Language patterns: AI vocabulary, copula avoidance, negative parallelisms, elegant variation, false ranges, passive voice
473
+ - Style patterns: em dashes (ZERO TOLERANCE), bold overuse, title case in headings
474
+ - Communication patterns: chatbot artifacts, sycophantic tone, filler phrases
475
+ Verify: zero em dashes, average sentence length under 15 words.
476
+
477
+ 4. **Concreteness fixer**
478
+ Load concreteness-fixer skill. Replace vague abstract NLM language with specific concrete details.
479
+ NLM tends toward generalities. Make everything specific.
480
+
481
+ 4.5. **Flow check (read-aloud simulation)**
482
+ Simulate reading the draft aloud, sentence by sentence.
483
+ Flow is 10x more important than grammar and punctuation (source: @TheCopyTitan).
484
+ A reader should never stumble, re-read, or feel jarred.
485
+
486
+ Check:
487
+ - Every sentence-to-sentence transition flows naturally (no abrupt topic jumps)
488
+ - One-breath rule: no sentence exceeds ~25 words (if you can't say it in one breath, split it)
489
+ - Opening words of consecutive sentences are varied (no "The... The... The..." repetition)
490
+ - Rhythm variation: mix short punchy sentences (3-7 words) with medium ones (10-20 words)
491
+ - No clunky word order that sounds wrong when spoken
492
+ - First sentence of each paragraph pulls the reader into the next
493
+
494
+ Fix every flagged spot. If a sentence reads awkwardly when spoken, rewrite it.
495
+ This catches what the humanizer misses: humanizer strips AI patterns per-sentence,
496
+ but flow is about how sentences connect sequentially.
497
+
498
+ 5. **Structure verification (Layer 3)**
499
+ - SEO heading hierarchy: H2 to H3, no skipped levels, keyword in first H2
500
+ - AEO pair exists (narrative + Q&A versions, both polished)
501
+ - AEO version uses PAA questions as H2 headings (verbatim from research)
502
+ - Schema markup: Article + FAQPage (HowTo for guides, ItemList for roundups)
503
+ - Internal links: insert per link-map.json and topic-clusters.json
504
+ - Link to parent WP category (= cornerstone/hub)
505
+ - Link to sibling nodes (related articles in same cluster branch)
506
+
507
+ 6. **Story verification (Layer 4)**
508
+ - Narrative arc: {{narrative_arc}}
509
+ - Emotional engagement: pain to hope to proof progression
510
+ - ICP language from research used naturally (verbatim phrases)
511
+ - Named frameworks included for shareability
512
+
513
+ 7. **Quality gate checklist (ALL must pass):**
514
+ - [ ] Approved hook is the title and opening line
515
+ - [ ] Narrative arc matches funnel stage spec
516
+ - [ ] AEO pair exists (narrative + Q&A versions)
517
+ - [ ] AEO H2s match PAA questions verbatim
518
+ - [ ] Schema markup present (Article + FAQPage + Author + BreadcrumbList)
519
+ - [ ] At least 1 proprietary/first-party data point
520
+ - [ ] Internal links match link-map.json + topic-clusters.json
521
+ - [ ] Zero em dashes in entire article
522
+ - [ ] Average sentence length under 15 words
523
+ - [ ] No AI vocabulary or patterns
524
+ - [ ] Brand voice DNA match verified
525
+ - [ ] H2 sections vary in length (at least 2x difference between longest and shortest)
526
+ - [ ] Paragraph lengths vary (mix of 1-sentence and 4+ sentence paragraphs)
527
+ - [ ] No two consecutive paragraphs are the same length
528
+ - [ ] Flow verified: read-aloud simulation passed (no clunky transitions)
529
+ - [ ] No sentence exceeds 25 words (one-breath rule)
530
+ - [ ] Opening words of consecutive sentences are varied
531
+ - [ ] SEO images generated with alt text, title attr, caption, keyword filenames
532
+ - [ ] Funnel metadata present (funnel_stage, buyer_stage, cta, icp_segment, narrative_arc)
533
+
534
+ If ANY check fails, fix it before proceeding to enrich.
535
+ Do NOT pass to enrich with validation failures.
536
+
537
+ Output: narrative-draft-polished.md + aeo-draft-polished.md
538
+ Save to the brand's content directory.
539
+ NEVER overwrite the raw narrative-draft.md or aeo-draft.md files.
540
+
541
+ # ── LAYER 4.5: INDEPENDENT QUALITY REVIEW ──────────────────
542
+
543
+ - id: quality_review
544
+ title: "Quality review: {{topic}}"
545
+ assignee: build
546
+ skills: [quality-gate, voice-brand-alignment, humanizer]
547
+ parents: [polish]
548
+ priority: 2
549
+ gate: true
550
+ deliverables: "businesses/{{tenant}}/content/_drafts/{{topic_slug}}/quality-review.md|200"
551
+ body: |
552
+ INDEPENDENT quality review of the polished drafts. This is NOT the same agent that polished.
553
+ This task catches what the polish agent missed. It is the last gate before enrichment.
554
+
555
+ Read the POLISHED files (NOT the raw NLM drafts):
556
+ - narrative-draft-polished.md
557
+ - aeo-draft-polished.md
558
+
559
+ Also load:
560
+ - The avoidance brief (from load_lessons task)
561
+ - Brand voice DNA (brand/voice-profile.md or brand/voice-dna.json)
562
+ - Research brief (keywords, PAA questions, pain phrases)
563
+
564
+ **REVIEW CHECKLIST (score each item PASS/FAIL with evidence):**
565
+
566
+ 1. **AI slop detection** — Does this read like AI-generated content?
567
+ - Check for: uniform paragraph lengths, generic transitions, vague language,
568
+ significance inflation, promotional tone, "delve/leverage/tapestry/navigate"
569
+ - If ANY AI pattern detected: FAIL with specific quote
570
+
571
+ 2. **Framework label detection** — Are there visible copywriting framework labels?
572
+ - Check for: PAS/AIDA/BAB/Shocking Fact/Fascination Bullets/Quotable Insight labels
573
+ - Check for: "Before (", "After (", "The Bridge", "Attention:", "Desire:", "Action:"
574
+ - If ANY label found: FAIL with specific quote
575
+
576
+ 3. **Em dash audit** — Count em dashes (—) in the entire article
577
+ - ZERO tolerance. Even one em dash = FAIL
578
+
579
+ 4. **Sentence length audit** — Average sentence length must be under 15 words
580
+ - Flag any sentence over 25 words (one-breath rule)
581
+
582
+ 5. **Voice DNA match** — Does the article sound like the brand?
583
+ - Compare against brand voice DNA
584
+ - Flag any voice drift
585
+
586
+ 6. **Structural variation** — Are sections varied in length?
587
+ - Longest section should be 2x+ the shortest
588
+ - No two consecutive paragraphs the same length
589
+ - If all sections are uniform: FAIL
590
+
591
+ 7. **Intent alignment** — Does the article answer what the searcher actually wants?
592
+ - Cross-check with the inferred intent from research
593
+ - If article misses the actual question: FAIL
594
+
595
+ 8. **Hook verification** — Is the approved hook the title and opening line?
596
+ - If NLM's original title is still there: FAIL
597
+
598
+ 9. **AEO verification** — Does the AEO version use PAA questions as H2 headings?
599
+ - Must be verbatim from research
600
+
601
+ 10. **Concreteness** — Are there specific numbers, named examples, real data?
602
+ - If everything is vague/general: FAIL
603
+
604
+ **OUTPUT: quality-review.md**
605
+ ```
606
+ ARTICLE: {{topic}}
607
+ DATE: [today]
608
+ REVIEWER: Independent quality review agent
609
+
610
+ RESULTS:
611
+ [For each check: PASS/FAIL + evidence]
612
+
613
+ OVERALL: PASS / FAIL
614
+
615
+ IF FAIL:
616
+ - Specific issues found (with quotes)
617
+ - Required fixes (what to change before enrich)
618
+ - Send back to polish task with fix instructions
619
+ ```
620
+
621
+ **GATE BEHAVIOR:**
622
+ - If OVERALL = PASS: proceed to enrich
623
+ - If OVERALL = FAIL: block enrich, send review back to polish task
624
+ with specific fix instructions. Polish agent re-runs and this review fires again.
625
+
626
+ # ── LAYER 5: ENRICHMENT ─────────────────────────────────────
627
+
628
+ - id: enrich
629
+ title: "Enrich: images + links + data: {{topic}}"
630
+ assignee: harper
631
+ skills: [image-gen, visual-style, creative]
632
+ parents: [quality_review]
633
+ priority: 2
634
+ deliverables: "businesses/{{tenant}}/content/_drafts/{{topic_slug}}/schema.jsonld|200;businesses/{{tenant}}/content/_drafts/{{topic_slug}}/image-metadata.json|100"
635
+ body: |
636
+ Enrich the polished article with visuals, data, and links.
637
+ This is Layer 5 of the Content Quality Standard.
638
+
639
+ **1. Featured/Hero Image (MANDATORY — Gemini Web Interface Method)**
640
+
641
+ The featured image is MANDATORY for every article. No article publishes without one.
642
+ Generate via Gemini web interface (no API keys needed). This is the proven method:
643
+
644
+ PREREQUISITE: Chrome must be running with remote debugging:
645
+ Chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.gemini-image-profile" \
646
+ --in-process-gpu --disable-gpu-sandbox --disable-gpu-compositing --disable-software-rasterizer
647
+
648
+ If Chrome is not logged into Google, inject cookies from the user's main Chrome profile:
649
+ pip3 install --break-system-packages browser_cookie3
650
+ python3 -c "import browser_cookie3,json; cj=browser_cookie3.chrome(domain_name='google.com'); ..."
651
+ Then inject via CDP Network.setCookie for each cookie.
652
+
653
+ GENERATION PIPELINE (via CDP WebSocket on ws://localhost:9222):
654
+ a) Read brand/DESIGN.md for the exact AI image prompt for this article's topic
655
+ b) Navigate to https://gemini.google.com/app (fresh conversation)
656
+ c) Focus the Quill editor: document.querySelector('div.ql-editor.textarea')?.focus()
657
+ d) Type prompt via CDP Input.dispatchKeyEvent (char by char — NOT JS .fill() or .value=)
658
+ e) Click Send: document.querySelector('button[aria-label="Send message"]')?.click()
659
+ f) Poll every 5s for up to 3min: querySelectorAll('img').filter(naturalWidth > 200 && !gstatic)
660
+ g) When image appears: draw to canvas, toDataURL('image/png'), extract via chunked CDP reads
661
+ h) Save raw PNG to /tmp/mg-images/post-{ID}-gemini.png
662
+
663
+ RESIZE to 1200x630 (featured image standard):
664
+ sips --resampleWidth 1200 input.png --out tmp.png && sips -c 630 1200 tmp.png --out final.png
665
+
666
+ UPLOAD to WordPress:
667
+ POST /wp-json/wp/v2/media (Content-Type: image/png, Content-Disposition: attachment)
668
+ Then POST /wp-json/wp/v2/posts/{ID} with { featured_media: mediaId }
669
+
670
+ Reference implementation: ~/.claude/skills/gemini-image/SKILL.md
671
+ Full pipeline tested and working as of 2026-07-17.
672
+
673
+ **2. In-content visuals (SEO-ready with full metadata)**
674
+ Read brand/creative-kit.md for visual identity. Read brand/DESIGN.md for exact tokens.
675
+ {{#icp_segment}}
676
+ Use the ICP-specific visual variant for {{icp_segment}} if defined in creative-kit.md.
677
+ {{/icp_segment}}
678
+ Generate 2-3 in-content visuals using the same Gemini web method or image-gen skill:
679
+ - Diagrams, infographics, data visualizations
680
+ - All images must match brand aesthetic from creative-kit.md and DESIGN.md
681
+
682
+ Image emphasis by funnel stage ({{funnel_stage}}):
683
+ - TOFU: Social share image + 1 in-content visual
684
+ - MOFU: Hero image + 2-3 in-content visuals (infographic preferred)
685
+ - BOFU: Product/lifestyle images
686
+
687
+ FOR EACH IMAGE generate full SEO metadata (save to image-metadata.json):
688
+ - alt_text: Descriptive, includes primary keyword naturally, under 125 chars.
689
+ Screen-reader friendly. Never keyword-stuffed.
690
+ - title_attr: Brief description of the image (different from alt text).
691
+ - caption: 1 sentence adding context for the reader.
692
+ - filename: primary-keyword-descriptor.jpg (hyphenated, lowercase, no spaces).
693
+ Hero: {primary-keyword}-hero.jpg. Infographic: {primary-keyword}-infographic.jpg.
694
+ - og_image: Hero image URL for Open Graph / Twitter Card meta.
695
+ - dimensions: 1200x630 for hero/OG. 1200x675 for in-content (16:9).
696
+ - compression: Target under 100KB per image for web performance.
697
+
698
+ **2. Schema markup generation (CRITICAL for rich snippets)**
699
+ Generate complete JSON-LD schema blocks. Save as schema.jsonld in the article directory.
700
+
701
+ a) Article schema:
702
+ {
703
+ "@context": "https://schema.org",
704
+ "@type": "Article",
705
+ "headline": "[approved hook/title]",
706
+ "datePublished": "[publish date ISO]",
707
+ "dateModified": "[publish date ISO]",
708
+ "author": { "@id": "[author schema URL]" },
709
+ "image": "[hero image URL]",
710
+ "publisher": { "@id": "[publisher URL]" },
711
+ "mainEntityOfPage": "[article URL]"
712
+ }
713
+
714
+ b) FAQPage schema (extract from the 5 FAQ Q&As in the article):
715
+ {
716
+ "@context": "https://schema.org",
717
+ "@type": "FAQPage",
718
+ "mainEntity": [
719
+ { "@type": "Question", "name": "[Q1]", "acceptedAnswer": { "@type": "Answer", "text": "[A1]" } },
720
+ ... (all 5 FAQs)
721
+ ]
722
+ }
723
+
724
+ c) Person (Author) schema (read from brand/author-bio.md):
725
+ {
726
+ "@context": "https://schema.org",
727
+ "@type": "Person",
728
+ "name": "[author name]",
729
+ "jobTitle": "[title]",
730
+ "url": "[author page URL]",
731
+ "image": "[author headshot URL]",
732
+ "sameAs": ["[social profile URLs]"],
733
+ "description": "[1-2 sentence bio]"
734
+ }
735
+
736
+ d) BreadcrumbList schema:
737
+ {
738
+ "@context": "https://schema.org",
739
+ "@type": "BreadcrumbList",
740
+ "itemListElement": [
741
+ { "position": 1, "name": "Home", "item": "[site URL]" },
742
+ { "position": 2, "name": "[category]", "item": "[category URL]" },
743
+ { "position": 3, "name": "[article title]", "item": "[article URL]" }
744
+ ]
745
+ }
746
+
747
+ Also generate HowTo or ItemList schema if article_type is how_to or alternatives respectively.
748
+
749
+ **2. First-party data**
750
+ Verify at least 1 proprietary data point is present in the article.
751
+ If none exists, flag it and use the best available: specific number, named outcome, unique comparison.
752
+
753
+ **3. External authority links**
754
+ Add minimum 3 outbound links to primary sources (studies, official docs, original research).
755
+ No link farms, no aggregators. Link to the ORIGINAL source.
756
+
757
+ **4. Internal links verification**
758
+ Verify internal links from topic-clusters.json are present:
759
+ - Link to parent WP category page (hub/cornerstone) for link juice concentration
760
+ - Link to sibling articles in same cluster branch
761
+ - Verify the article has been assigned to the correct WP category (parent hub keyword)
762
+
763
+ **5. Backlink targets**
764
+ Produce a list of 5-10 sites that should link to this article (for off-page SEO outreach).
765
+
766
+ Output: enriched narrative-draft.md + enriched aeo-draft.md + image files + image-metadata.json + schema.jsonld + backlink-targets.md
767
+
768
+ # ── LAYER 6: PUBLISH ────────────────────────────────────────
769
+
770
+ - id: publish
771
+ title: "Publish to WordPress: {{topic}}"
772
+ assignee: harper
773
+ skills: [blog-publish-pipeline]
774
+ parents: [enrich]
775
+ priority: 1
776
+ body: |
777
+ Publish the validated article to WordPress.
778
+ This is Layer 6 of the Content Quality Standard.
779
+
780
+ CRITICAL: Read the POLISHED files, NOT the raw NLM drafts:
781
+ - Read: narrative-draft-polished.md (NOT narrative-draft.md)
782
+ - Read: aeo-draft-polished.md (NOT aeo-draft.md)
783
+ The raw drafts are unpolished NLM output. The polished files have hooks, voice DNA,
784
+ humanizer, schema, and quality gate applied. Publishing raw drafts = publishing slop.
785
+
786
+ Load the blog-publish-pipeline skill and follow its workflow.
787
+
788
+ **1. WP Category assignment**
789
+ Read brand/topic-clusters.json. Find this article's parent node.
790
+ The parent node's keyword = the WP category name.
791
+ If the category does not exist in WP, create it:
792
+ POST /wp-json/wp/v2/categories {"name": "<parent keyword>", "slug": "<parent-slug>"}
793
+ Assign the article to this category. NEVER use "uncategorized".
794
+
795
+ **2. Upload images with SEO metadata**
796
+ Read image-metadata.json from the enrich task.
797
+ Upload each image via WP REST API:
798
+ POST /wp-json/wp/v2/media (multipart form data)
799
+ For each image, set in the upload payload:
800
+ - alt_text: from image-metadata.json alt_text field
801
+ - title: from image-metadata.json title_attr field
802
+ - caption: from image-metadata.json caption field
803
+ - description: brief description for media library
804
+ Record the returned media IDs for in-content image references.
805
+
806
+ **3. Publish narrative version first**
807
+ - Post via WP REST API (POST /wp-json/wp/v2/posts)
808
+ - Set status `publish` (or `draft` for review)
809
+ - Article body rules: NO `<h1>` (WP adds its own), NO inline hero `<img>` at start,
810
+ inject scoped CSS for readability
811
+ - In-content images: use the media IDs from step 2 with proper alt_text attributes
812
+ - Set featured image (hero media ID from step 2)
813
+ - Set category to the parent hub category (from topic-clusters.json)
814
+ - If language is not `en`: append `-{lang}` to filename, set Polylang language term
815
+ - If slug conflicts (gets `-2` suffix), fix via WP REST API
816
+
817
+ **3. Publish AEO version second**
818
+ - Use narrative's URL in the AEO handshake div
819
+ - No featured image on AEO. No author box on AEO.
820
+ - Orange handshake div linking to narrative.
821
+ - Set AEO category (typically Guides or equivalent).
822
+
823
+ **4. Cross-link handshake divs**
824
+ Update both posts with real URLs (replace placeholders).
825
+ - Narrative: blue handshake div links to AEO
826
+ - AEO: orange handshake div links to narrative
827
+
828
+ **5. AIOSEO metadata** (MANDATORY on narrative version):
829
+ Set via WP REST API PATCH with `aioseo_meta_data`:
830
+ - `description`: 120-160 chars, include target keyword near start
831
+ - `keyphrases`: object (NOT string): `{ "focus": { "keyphrase": "primary keyword" }, "additional": ["secondary", "tertiary"] }`
832
+ - `og_title`, `og_description`, `twitter_title`, `twitter_description`
833
+
834
+ **6. Schema injection** (MANDATORY on narrative version):
835
+ Read schema.jsonld from the enrich task. Inject all JSON-LD blocks into the post:
836
+ - If AIOSEO is available: set via `aioseo_meta_data.schema` (base64-encoded JSON-LD)
837
+ - Otherwise: inject as raw HTML in a Custom HTML block at the end of the post content:
838
+ `<script type="application/ld+json">{ ... }</script>`
839
+ - Inject Article, FAQPage, Person (Author), and BreadcrumbList schemas
840
+ - Verify by fetching the published page and checking for the script tags
841
+
842
+ **7. Article CTA**
843
+ The article CTA links to the lead magnet (NOT a MailPoet form embedded in the article).
844
+ The lead magnet page has the MailPoet subscription form.
845
+ CTA text depends on funnel stage:
846
+ - TOFU: "Get the free [lead magnet name]" → links to lead magnet page
847
+ - MOFU: "See the full guide" → links to cornerstone
848
+ - BOFU: conversion action → links to landing page
849
+
850
+ **8. Update topic cluster status**
851
+ After successful publish, update brand/topic-clusters.json:
852
+ - Set node status to "published"
853
+ - Set article_url to the published narrative URL
854
+ - Set aeo_url to the published AEO URL
855
+ - Set wp_post_id and wp_category_id
856
+
857
+ Save the published article's post IDs and URLs.
858
+
859
+ # ── LAYER 7: INDEX ──────────────────────────────────────────
860
+
861
+ - id: indexing
862
+ title: "Submit to Google/Bing: {{topic}}"
863
+ assignee: harper
864
+ skills: [auto-indexing]
865
+ parents: [publish]
866
+ priority: 1
867
+ body: |
868
+ Submit the published articles to search engines for indexing.
869
+ This is Layer 7 of the Content Quality Standard.
870
+
871
+ 1. Run the auto-indexing skill (IndexNow notifies Google, Bing, Yandex, Seznam + sitemap pings)
872
+ 2. Submit BOTH the narrative and AEO version URLs
873
+ 3. Verify submission succeeded (check API responses)
874
+ 4. Note: full indexing takes 24-48h. Verify via Google Search Console URL Inspection after 48h.
875
+
876
+ # ── LAYER 8: DISTRIBUTE ─────────────────────────────────────
877
+
878
+ - id: socialize
879
+ title: "Social campaign: {{topic}}"
880
+ assignee: social
881
+ skills: [nlm-cli, hook-generator, voice-brand-alignment, humanizer, content-atomizer, social-campaign, content-repost-manager, medium-publisher, linkedin-publisher]
882
+ parents: [indexing]
883
+ priority: 1
884
+ body: |
885
+ Generate and distribute a balanced social campaign for this article.
886
+ This is Layer 8 of the Content Quality Standard.
887
+ Every social post links to the article as the path to solve the user's pain.
888
+
889
+ {{#funnel_id}}
890
+ FUNNEL: All social posts serve funnel {{funnel_id}}. Use the funnel's CTA.
891
+ Each post must include: funnel_id={{funnel_id}}, buyer_stage, cta, icp_segment.{{/funnel_id}}
892
+
893
+ **INPUTS:**
894
+ - icp_segment: {{icp_segment}} (or "general")
895
+ - funnel_stage: {{funnel_stage}}
896
+ - keyword_intent: from research brief (informational/evaluation/transactional)
897
+ - social_mix: read from brand/funnel-map.md (recommended TOFU/MOFU/BOFU ratio)
898
+ - blog_article_url: from publish task
899
+ - NLM notebook_id: from nlm_draft task (for querying social angles)
900
+
901
+ **STAGE 1: NLM generates raw social angles (per platform)**
902
+
903
+ NLM provides grounded insight angles. Our skills layer adds hooks, voice DNA, humanizer.
904
+
905
+ For EACH platform, query NLM for raw angles:
906
+ ```
907
+ notebooklm generate report --format custom --wait --json \
908
+ "Create [N] social media post angles for [PLATFORM] about {{topic}}.
909
+ Target audience: {{icp_segment}}.
910
+ Funnel stage: {{funnel_stage}}.
911
+
912
+ For each angle provide:
913
+ - A hook (attention grabber)
914
+ - A key insight with data from your sources
915
+ - A CTA pointing to this URL: [blog_article_url]
916
+
917
+ Platform constraints:
918
+ [X/Twitter: 280 chars max, hook-first, include 1 data point, end with implied CTA]
919
+ [Threads: 500 chars max, conversational, observational tone]
920
+ [Bluesky: 300 chars max, community-oriented, share insights as observations]
921
+ [Reddit: value-first, genuine, NOT promotional. Include target subreddit suggestion.]
922
+ Language: {{language}}"
923
+ ```
924
+
925
+ For LONG-FORM teaser platforms:
926
+ ```
927
+ notebooklm generate report --format custom --wait --json \
928
+ "Create a [PLATFORM] teaser article about {{topic}}.
929
+ This is NOT the full article. It is a SHORTER-FORM TEASER (300-500 words for LinkedIn,
930
+ 500-800 words for Medium, 300-500 words for Substack).
931
+
932
+ Share ONE key insight from your sources. Create a curiosity gap.
933
+ End with: 'Read the full breakdown: [blog_article_url]'
934
+
935
+ Platform tone:
936
+ [LinkedIn: professional, impact-focused for {{icp_segment}}]
937
+ [Medium: editorial, executive summary style]
938
+ [Substack: personal, conversational newsletter style]
939
+ Language: {{language}}"
940
+ ```
941
+
942
+ **STAGE 2: Our system processes raw angles**
943
+
944
+ FOR EACH platform's raw angles:
945
+ 1. hook-generator: craft platform-native hooks (tournament scored)
946
+ Select hooks that match funnel_stage + ICP + keyword intent
947
+ 2. voice-brand-alignment: apply brand voice DNA
948
+ Load voice DNA from brand/voice-profile.md or brand/voice-dna.json
949
+ NLM does not know the brand voice. Our voice DNA system enforces it.
950
+ 3. humanizer: strip any AI patterns from NLM-sourced language
951
+ 4. Platform format verification (character limits, hashtag norms, link placement)
952
+ 5. Flow check (read-aloud simulation): Read each post aloud (simulate).
953
+ Social copy lives or dies on flow. If it doesn't sound natural spoken, it won't land.
954
+ Flag and rewrite any post that feels clunky, has awkward word order, or exceeds one breath.
955
+ Check opening-word variety across consecutive posts in a thread.
956
+
957
+ **STAGE 3: Campaign assembly (balanced per social_mix)**
958
+
959
+ Assemble the balanced campaign using the ratio from the funnel map:
960
+ - TOFU posts (awareness): pain point hooks, stat callouts, "did you know" angles
961
+ - MOFU posts (evaluation): comparison angles, framework explanations, data-backed insights
962
+ - BOFU posts (conversion): social proof, product angle, urgency
963
+
964
+ Every post links to blog_article_url as the path to solve the user's pain.
965
+
966
+ **STAGE 4: Long-form publishing**
967
+
968
+ - LinkedIn: publish via linkedin-publisher skill
969
+ - Medium: publish via medium-publisher skill
970
+ - Substack: publish via API or manual
971
+ - Reddit: genuine community posts in targeted subreddits
972
+ Use the pain phrases from research as the hook
973
+ Share the key finding, mention the full breakdown with link
974
+ Frame as helping the community, not self-promotion
975
+
976
+ **STAGE 5: Repurposing schedule**
977
+ Load content-repost-manager skill. Create an evergreen reposting schedule.
978
+
979
+ # ── LAYER 9: LINK NETWORK ───────────────────────────────────
980
+
981
+ - id: linkbacks
982
+ title: "Build link network: {{topic}}"
983
+ assignee: linker
984
+ skills: [off-page-seo, link-building-seo]
985
+ parents: [socialize]
986
+ priority: 1
987
+ deliverables: "businesses/{{tenant}}/content/_drafts/{{topic_slug}}/link-building-action-plan.md|500"
988
+ body: |
989
+ Build internal and external links for the published article.
990
+ This is Layer 9 of the Content Quality Standard.
991
+
992
+ **1. Internal link verification**
993
+ Verify the article contains all internal links from topic-clusters.json:
994
+ - Links to parent WP category (hub/cornerstone) for link juice concentration
995
+ - Links to sibling articles in same cluster branch
996
+ Verify that the parent category page and sibling articles link back to this article.
997
+
998
+ **2. External backlinks**
999
+ Load off-page-seo and link-building-seo skills.
1000
+ From the backlink targets identified in the enrich task:
1001
+ - Directory submission opportunities
1002
+ - Guest post targets
1003
+ - Social profile link opportunities
1004
+ - Resource page link opportunities
1005
+
1006
+ **3. Produce link building action plan**
1007
+ For each target: site name, URL, link type, contact method, priority.
1008
+
1009
+ **4. Social signals**
1010
+ Ensure social posts from the socialize task are live and generating engagement signals.
1011
+
1012
+ **5. Topic cluster update**
1013
+ Update brand/topic-clusters.json: mark any deep-dive child nodes as candidates
1014
+ for future content production based on article performance.
1015
+
1016
+ Output: link-building-action-plan.md with prioritized targets.
1017
+
1018
+ # ── LAYER 10: TRACKING ──────────────────────────────────────
1019
+
1020
+ - id: tracking
1021
+ title: "Track article: {{topic}}"
1022
+ assignee: tracker
1023
+ skills: [analytics-tracking]
1024
+ parents: [linkbacks]
1025
+ priority: 1
1026
+ body: |
1027
+ Record this article in the brand's content tracking system.
1028
+ Update brand/topic-clusters.json with publication status.
1029
+
1030
+ **1. Update content-tracker.json:**
1031
+ Append entry:
1032
+ {
1033
+ "topic": "{{topic}}",
1034
+ "slug": "{slug}",
1035
+ "published_date": "[today]",
1036
+ "narrative_url": "[URL]",
1037
+ "aeo_url": "[URL]",
1038
+ "wp_post_id": [ID],
1039
+ "keyword": "[primary keyword]",
1040
+ "funnel_id": "{{funnel_id}}",
1041
+ "funnel_stage": "{{funnel_stage}}",
1042
+ "icp_segment": "{{icp_segment}}",
1043
+ "cluster_node_id": "{{cluster_node_id}}",
1044
+ "social_posts": { "twitter": "[URL]", "linkedin": "[URL]", "reddit": "[URL]", "medium": "[URL]" },
1045
+ "status": "published",
1046
+ "review_result": null,
1047
+ "learnings": null
1048
+ }
1049
+ Save to: businesses/{{tenant}}/content/content-tracker.json
1050
+
1051
+ **2. Update topic-clusters.json:**
1052
+ Set node status to "published" with article URLs.
1053
+
1054
+ **3. Set up Google Search Console tracking:**
1055
+ Note the URL for 30-day ranking check (automated follow-up).
1056
+
1057
+ # ── LAYER 11: LEARN + FEEDBACK LOOP ─────────────────────────
1058
+
1059
+ - id: learn
1060
+ title: "Learn from review: {{topic}}"
1061
+ assignee: reviewer
1062
+ skills: []
1063
+ parents: [tracking]
1064
+ priority: 1
1065
+ body: |
1066
+ Capture what worked or failed. Feed lessons into Inkstone + brand evergreen.
1067
+ This prevents repeating mistakes and reinforces winning patterns.
1068
+ This is the final layer — the system gets smarter with every article.
1069
+
1070
+ This task fires in TWO modes:
1071
+ - APPROVAL MODE: When Jairo approves the article during batch review
1072
+ - REJECTION MODE: When Jairo rejects or requests revisions
1073
+
1074
+ ## IF REJECTED:
1075
+
1076
+ **1. CAPTURE rejection reason (structured):**
1077
+ Present to Jairo: "What was wrong with '{{topic}}'?"
1078
+ Common categories (Jairo picks or describes):
1079
+ - voice_drift: doesn't sound like the brand
1080
+ - thin_content: lacks depth or substance
1081
+ - ai_slop: detectable AI patterns despite humanizer
1082
+ - wrong_intent: doesn't answer what the searcher wants
1083
+ - uniform_structure: all sections same length (the Harper bug)
1084
+ - missing_schema: no FAQ/Author schema for snippets
1085
+ - poor_images: images missing or no SEO metadata
1086
+ - factual_error: wrong information
1087
+ - weak_hook: title/opening doesn't grab
1088
+ - other: [Jairo describes]
1089
+
1090
+ Specific issue: [what exactly was wrong]
1091
+ Example passage: [quote the offending section]
1092
+ Fix: [what should have been done instead]
1093
+
1094
+ **2. WRITE TO INKSTONE (failure pattern):**
1095
+ ```
1096
+ inkstone_memory_write(
1097
+ text="REJECTION for {{tenant}}: [category].
1098
+ Article: {{topic}}. Date: [today].
1099
+ Issue: [specific issue].
1100
+ Example: [passage].
1101
+ Fix: [correction].
1102
+ This is the Nth occurrence of this pattern.",
1103
+ namespace="/business/{{tenant}}/content-quality",
1104
+ knowledgeType="failure_pattern",
1105
+ confidence=0.9
1106
+ )
1107
+ ```
1108
+
1109
+ **3. UPDATE BRAND EVERGREEN:**
1110
+ Append to businesses/{{tenant}}/evergreen.md under "## Content Quality Lessons":
1111
+ ```
1112
+ - [date] [category]: [lesson] (Article: {{topic}})
1113
+ ```
1114
+
1115
+ **4. ESCALATE if recurring:**
1116
+ If this pattern has occurred 3+ times (check Inkstone), add a HARD RULE to:
1117
+ - businesses/{{tenant}}/brand/voice-profile.md (voice-related)
1118
+ - businesses/{{tenant}}/brand/skill-context.json (structural/quality)
1119
+
1120
+ **5. QUEUE for rework:**
1121
+ Add article to the rework queue with specific fix instructions.
1122
+ The avoidance brief (Layer -1) will catch this on the next run.
1123
+
1124
+ ## IF APPROVED:
1125
+
1126
+ **1. CAPTURE what worked:**
1127
+ Ask Jairo: "What landed with '{{topic}}'?"
1128
+ - Which hook pattern won?
1129
+ - Which voice elements resonated?
1130
+ - Which framework drove the structure?
1131
+ - What data point or insight was strongest?
1132
+ - Was single article or thread better for socials?
1133
+
1134
+ **2. WRITE TO INKSTONE (success pattern):**
1135
+ ```
1136
+ inkstone_memory_write(
1137
+ text="SUCCESS for {{tenant}}: [what worked].
1138
+ Article: {{topic}}. Date: [today].
1139
+ Hook: [pattern]. Framework: [name].
1140
+ Insight: [what resonated].
1141
+ Social format: [what worked].",
1142
+ namespace="/business/{{tenant}}/content-quality",
1143
+ knowledgeType="success_pattern",
1144
+ confidence=0.85
1145
+ )
1146
+ ```
1147
+
1148
+ **3. REINFORCE winning patterns:**
1149
+ - If a hook pattern won 3+ times: add to brand hook templates
1150
+ - If a voice element landed: reinforce in voice-profile.md
1151
+ - If a structure worked: prioritize in future article planning
1152
+
1153
+ ## ALWAYS:
1154
+
1155
+ **4. UPDATE content-tracker.json:**
1156
+ Set review_result: "approved" or "rejected" + learnings field with summary.
1157
+
1158
+ **5. INTELLIGENCE OUTPUT:**
1159
+ The avoidance brief (Layer -1) on the NEXT article for this brand
1160
+ will automatically pick up these lessons from Inkstone.
1161
+ The system is now smarter than it was before this article.