Documentation Index
Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
Use this file to discover all available pages before exploring further.
What this solves
A weekly digest email has three placements: hero, body card, footer. If contact-policy fatigue or qualification rules empty the hero, you don’t want to send a footer-only email — that’s worse than not sending. Atomic coupling lets you say “this channel is all-or-nothing within itself” without forcing every flow on that channel to be atomic.Why this works
Coupling is per-channel, not global. EachChannel.couplingMode defaults to "partial" (any non-empty placement ships). Switching it to "atomic" makes the post-group coupling pass scan that channel’s placement results; if any one is empty, the rest are emptied with a cascade record in trace.channelCoupling[].
Cross-channel coupling is intentionally not supported — different channels are different attention surfaces, and an empty SMS shouldn’t suppress an email.
Three knobs
| Setting | Where | Effect |
|---|---|---|
Channel.couplingMode | POST/PUT /api/v1/channels | Default for the channel: "partial" (default) or "atomic". |
DecisionFlow.couplingOverride | PUT /api/v1/decision-flows | Per-flow override — beats the channel default when set. Useful when one channel serves both atomic (digest) and partial (transactional) flows. |
placementResults[*].count === 0 | derived | The trigger: zero offers in a placement, after group allocation. |
Step 1 — Mark the email channel atomic
Step 2 — Fire a multi-placement recommend
What you’ll see when coupling fires
Response body:<email-body-card> would have had offers — because the hero was empty and the channel is atomic. The cascaded: true flag in channelCoupling[] is your audit trail: this wasn’t “no offers were available,” this was “we deliberately suppressed.”
Why not just use Group.allowPartial?
Group.allowPartial was the old switch. It’s deprecated and no-op now (the field still parses to keep legacy IRs valid). It conflated two distinct cases:
- “Operator chose to require all placements filled” (now:
Channel.couplingMode = "atomic") - “Contact policy fatigue happened to empty everything” (now: surfaces in the trace’s
contactPolicyResults[]regardless)
allowPartial could only do flow-wide all-or-nothing.
Per-flow override
A weekly-digest flow needs atomic; a same-channel transactional alert flow needs partial. Set the channel default to atomic, then override on the transactional flow:Proof reference
T8 of the proof bundle:channelCoupling.cascaded = true with both placements empty, when the hero is fatigued out by a frequency cap.