fix: stop chapter_production from dispatching project_index mid-book

- Remove project_index dispatch from chapter_production.yml entirely.
  chapter_production should ONLY dispatch book_chapter. The project_index
  dispatch belongs to book_editorial (which already handles it correctly).
  This was the root cause of the CLM hallucination cascade:
    pulse_check -> project_review -> chapter_production -> project_index -> marketing_campaign (infinite loop)

- Add clm_dispatch_gate guard in project_index.yml. project_index now checks
  whether its task message is a real book-complete signal or a generic/none
  message. If gate is 'skip', clm_dispatch_content is 'none' and the enqueue
  call returns gracefully (optional: true). Only fires when triggered by
  book_editorial with a legitimate completion message.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Baity
2026-04-28 20:02:41 -04:00
parent f023926944
commit 1bff66b674
2 changed files with 56 additions and 46 deletions

View File

@@ -108,25 +108,6 @@ steps:
task_type: "book_chapter"
content: "{chapter_dispatch_message}"
# If COMPLETE, dispatch project_index to compile the manuscript and signal book done
- type: think
max_tokens: 50
output_key: index_dispatch_message
hint: |
Read production_status above.
If production_status is COMPLETE, output exactly:
Compile the manuscript index for {project.name}. All chapters are polished and complete.
If production_status is NEEDS_CHAPTER, output exactly: none
- type: tool
action: enqueue_strategy
optional: true
params:
company_slug: "crimson_leaf_publishing"
project_slug: "{project.slug}"
task_type: "project_index"
content: "{index_dispatch_message}"
- type: reply
target: channel
channel_name: "crimson_leaf_publishing:live-feed"

View File

@@ -74,16 +74,32 @@ steps:
- type: document
filename: "MANUSCRIPT-README"
# Sprint 81 Loop 2: when a book is complete, automatically dispatch a marketing campaign
# to Crimson Leaf Marketing. optional: true so that if dispatch fails, the book is still done.
- type: tool
action: enqueue_strategy
optional: true
params:
company_slug: "crimson_leaf_marketing"
project_slug: "book-marketing-agency"
task_type: "marketing_campaign"
content: |
# Guard: only dispatch CLM marketing if this project_index was triggered as a genuine
# book-complete event. If the task message is "none", generic, or empty (e.g. from
# chapter_production running mid-book), skip the dispatch.
- type: think
max_tokens: 50
output_key: clm_dispatch_gate
hint: |
Read the task message above (the original message this task was given).
If the message is blank, "none", or a generic system message (e.g. "Compile the manuscript
index", "Pulse-check", "Project review"), output exactly: skip
If the message contains a real book title, author, genre, and/or an explicit marketing
request, output exactly: dispatch
Output ONLY one word: skip or dispatch
# Assemble the final CLM dispatch content. If gate is "skip", outputs "none" so the
# server-side handler finds no real payload and returns gracefully (optional: true).
- type: think
max_tokens: 600
output_key: clm_dispatch_content
hint: |
The dispatch gate is: {clm_dispatch_gate}
If the gate is "skip", output exactly: none
If the gate is "dispatch", write the full marketing campaign request:
MARKETING CAMPAIGN REQUEST -- {project.name}
The manuscript for "{project.name}" is complete and ready for marketing.
@@ -109,6 +125,19 @@ steps:
MISSION: Get this book discovered by its audience and generate revenue for Crimson Leaf.
Output ONLY the content text above (or "none" if gate is skip).
# Sprint 81 Loop 2: dispatch marketing only when clm_dispatch_content is a real brief.
# optional: true -- if content is "none" the handler returns gracefully.
- type: tool
action: enqueue_strategy
optional: true
params:
company_slug: "crimson_leaf_marketing"
project_slug: "book-marketing-agency"
task_type: "marketing_campaign"
content: "{clm_dispatch_content}"
- type: close
rag_update: true